From 7dfbfdcb01431ab18c19420c696972639214a21f Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Tue, 23 Sep 2025 12:28:42 +0100 Subject: [PATCH] Report overall cache usage for CodeQL dependency caches --- lib/init-action-post.js | 41 ++++++++++++++++++++++++++++++---- src/api-client.ts | 2 +- src/dependency-caching.ts | 34 +++++++++++++++++++++++++++- src/init-action-post-helper.ts | 4 ++++ src/init-action-post.ts | 22 +++++++++++++++++- 5 files changed, 96 insertions(+), 7 deletions(-) diff --git a/lib/init-action-post.js b/lib/init-action-post.js index 7b85c8e0a..9ea1459a9 100644 --- a/lib/init-action-post.js +++ b/lib/init-action-post.js @@ -128487,6 +128487,18 @@ function computeAutomationID(analysis_key, environment) { } return automationID; } +async function listActionsCaches(key, ref) { + const repositoryNwo = getRepositoryNwo(); + return await getApiClient().paginate( + "GET /repos/{owner}/{repo}/actions/caches", + { + owner: repositoryNwo.owner, + repo: repositoryNwo.repo, + key, + ref + } + ); +} function wrapApiConfigurationError(e) { if (isHTTPError(e)) { if (e.message.includes("API rate limit exceeded for installation") || e.message.includes("commit not found") || e.message.includes("Resource not accessible by integration") || /ref .* not found in this repository/.test(e.message)) { @@ -128500,6 +128512,9 @@ function wrapApiConfigurationError(e) { return e; } +// src/caching-utils.ts +var core6 = __toESM(require_core()); + // src/codeql.ts var fs13 = __toESM(require("fs")); var path13 = __toESM(require("path")); @@ -128771,9 +128786,6 @@ var CodeQuality = { sentinelPrefix: "CODEQL_UPLOAD_QUALITY_SARIF_" }; -// src/caching-utils.ts -var core6 = __toESM(require_core()); - // src/config/db-config.ts var semver2 = __toESM(require_semver2()); var PACK_IDENTIFIER_PATTERN = (function() { @@ -131123,6 +131135,22 @@ var core11 = __toESM(require_core()); // src/dependency-caching.ts var actionsCache3 = __toESM(require_cache3()); var glob = __toESM(require_glob3()); +var CODEQL_DEPENDENCY_CACHE_PREFIX = "codeql-dependencies"; +async function getDependencyCacheUsage(logger) { + try { + const caches = await listActionsCaches(CODEQL_DEPENDENCY_CACHE_PREFIX); + const totalSize = caches.reduce( + (acc, cache) => acc + (cache.size_in_bytes ?? 0), + 0 + ); + return { count: caches.length, size_bytes: totalSize }; + } catch (err) { + logger.warning( + `Unable to retrieve information about dependency cache usage: ${getErrorMessage(err)}` + ); + } + return void 0; +} // src/analyze.ts function dbIsFinalized(config, language, logger) { @@ -133672,6 +133700,7 @@ async function runWrapper() { const startedAt = /* @__PURE__ */ new Date(); let config; let uploadFailedSarifResult; + let dependencyCachingUsage; try { restoreInputs(); const gitHubVersion = await getGitHubVersion(); @@ -133699,6 +133728,9 @@ async function runWrapper() { features, logger ); + if (await isAnalyzingDefaultBranch() && config.dependencyCachingEnabled !== "none" /* None */) { + dependencyCachingUsage = await getDependencyCacheUsage(logger); + } } } catch (unwrappedError) { const error2 = wrapError(unwrappedError); @@ -133732,7 +133764,8 @@ async function runWrapper() { const statusReport = { ...statusReportBase, ...uploadFailedSarifResult, - job_status: getFinalJobStatus() + job_status: getFinalJobStatus(), + dependency_caching_usage: JSON.stringify(dependencyCachingUsage ?? {}) }; logger.info("Sending status report for init-post step."); await sendStatusReport(statusReport); diff --git a/src/api-client.ts b/src/api-client.ts index 8e4a30c57..555a46bb4 100644 --- a/src/api-client.ts +++ b/src/api-client.ts @@ -214,7 +214,7 @@ export interface ActionsCacheItem { /** List all Actions cache entries matching the provided key and ref. */ export async function listActionsCaches( key: string, - ref: string, + ref?: string, ): Promise { const repositoryNwo = getRepositoryNwo(); diff --git a/src/dependency-caching.ts b/src/dependency-caching.ts index 819651670..aca7b9ae7 100644 --- a/src/dependency-caching.ts +++ b/src/dependency-caching.ts @@ -5,12 +5,13 @@ import * as actionsCache from "@actions/cache"; import * as glob from "@actions/glob"; import { getTemporaryDirectory } from "./actions-util"; +import { listActionsCaches } from "./api-client"; import { getTotalCacheSize } from "./caching-utils"; import { Config } from "./config-utils"; import { EnvVar } from "./environment"; import { KnownLanguage, Language } from "./languages"; import { Logger } from "./logging"; -import { getRequiredEnvParam } from "./util"; +import { getErrorMessage, getRequiredEnvParam } from "./util"; /** * Caching configuration for a particular language. @@ -344,3 +345,34 @@ async function cachePrefix( return `${prefix}-${CODEQL_DEPENDENCY_CACHE_VERSION}-${runnerOs}-${language}-`; } + +/** Represents information about our overall cache usage for CodeQL dependency caches. */ +export interface DependencyCachingUsageReport { + count: number; + size_bytes: number; +} + +/** + * Tries to determine the overall cache usage for CodeQL dependencies caches. + * + * @param logger The logger to log errors to. + * @returns Returns the overall cache usage for CodeQL dependencies caches, or `undefined` if we couldn't determine it. + */ +export async function getDependencyCacheUsage( + logger: Logger, +): Promise { + try { + const caches = await listActionsCaches(CODEQL_DEPENDENCY_CACHE_PREFIX); + const totalSize = caches.reduce( + (acc, cache) => acc + (cache.size_in_bytes ?? 0), + 0, + ); + return { count: caches.length, size_bytes: totalSize }; + } catch (err) { + logger.warning( + `Unable to retrieve information about dependency cache usage: ${getErrorMessage(err)}`, + ); + } + + return undefined; +} diff --git a/src/init-action-post-helper.ts b/src/init-action-post-helper.ts index 0d21bd3b6..3b1d6aea7 100644 --- a/src/init-action-post-helper.ts +++ b/src/init-action-post-helper.ts @@ -45,6 +45,10 @@ export interface JobStatusReport { job_status: JobStatus; } +export interface DependencyCachingUsageReport { + dependency_caching_usage?: string; +} + function createFailedUploadFailedSarifResult( error: unknown, ): UploadFailedSarifResult { diff --git a/src/init-action-post.ts b/src/init-action-post.ts index b7c0e92a4..74759bb3c 100644 --- a/src/init-action-post.ts +++ b/src/init-action-post.ts @@ -12,10 +12,16 @@ import { printDebugLogs, } from "./actions-util"; import { getGitHubVersion } from "./api-client"; +import { CachingKind } from "./caching-utils"; import { getCodeQL } from "./codeql"; import { Config, getConfig } from "./config-utils"; import * as debugArtifacts from "./debug-artifacts"; +import { + DependencyCachingUsageReport, + getDependencyCacheUsage, +} from "./dependency-caching"; import { Features } from "./feature-flags"; +import * as gitUtils from "./git-utils"; import * as initActionPostHelper from "./init-action-post-helper"; import { getActionsLogger } from "./logging"; import { getRepositoryNwo } from "./repository"; @@ -32,7 +38,8 @@ import { checkDiskUsage, checkGitHubVersionInRange, wrapError } from "./util"; interface InitPostStatusReport extends StatusReportBase, initActionPostHelper.UploadFailedSarifResult, - initActionPostHelper.JobStatusReport {} + initActionPostHelper.JobStatusReport, + initActionPostHelper.DependencyCachingUsageReport {} async function runWrapper() { const logger = getActionsLogger(); @@ -41,6 +48,7 @@ async function runWrapper() { let uploadFailedSarifResult: | initActionPostHelper.UploadFailedSarifResult | undefined; + let dependencyCachingUsage: DependencyCachingUsageReport | undefined; try { // Restore inputs from `init` Action. restoreInputs(); @@ -73,6 +81,17 @@ async function runWrapper() { features, logger, ); + + // If we are analysing the default branch and some kind of caching is enabled, + // then try to determine our overall cache usage for dependency caches. We only + // do this under these circumstances to avoid slowing down analyses for PRs + // and where caching may not be enabled. + if ( + (await gitUtils.isAnalyzingDefaultBranch()) && + config.dependencyCachingEnabled !== CachingKind.None + ) { + dependencyCachingUsage = await getDependencyCacheUsage(logger); + } } } catch (unwrappedError) { const error = wrapError(unwrappedError); @@ -109,6 +128,7 @@ async function runWrapper() { ...statusReportBase, ...uploadFailedSarifResult, job_status: initActionPostHelper.getFinalJobStatus(), + dependency_caching_usage: JSON.stringify(dependencyCachingUsage ?? {}), }; logger.info("Sending status report for init-post step."); await sendStatusReport(statusReport);