Clean up old TRAP caches

This commit is contained in:
Henry Mercer
2024-05-21 23:05:05 +01:00
parent 898dead2d6
commit 610e72285f
7 changed files with 121 additions and 26 deletions

View File

@@ -4,7 +4,6 @@ import * as retry from "@octokit/plugin-retry";
import consoleLogLevel from "console-log-level";
import { getActionVersion, getRequiredInput } from "./actions-util";
import { Logger } from "./logging";
import { parseRepositoryNwo } from "./repository";
import {
ConfigurationError,
@@ -198,27 +197,42 @@ export function computeAutomationID(
}
export interface ActionsCacheItem {
key?: string | undefined;
created_at?: string;
id?: number;
key?: string;
size_in_bytes?: number;
}
/** List all Actions cache entries matching the provided key and ref. */
export async function listActionsCaches(
key: string,
ref: string,
logger: Logger,
): Promise<ActionsCacheItem[]> {
const repositoryNwo = parseRepositoryNwo(
getRequiredEnvParam("GITHUB_REPOSITORY"),
);
logger.debug(`Retrieving Actions caches for key ${key} and ref ${ref}`);
return await getApiClient().paginate(
"GET /repos/{owner}/{repo}/actions/caches",
{
owner: repositoryNwo.owner,
repo: repositoryNwo.repo,
key,
ref,
},
);
}
const apiClient = getApiClient();
return await apiClient.paginate("GET /repos/{owner}/{repo}/actions/caches", {
/** Delete an Actions cache item by its ID. */
export async function deleteActionsCache(id: number) {
const repositoryNwo = parseRepositoryNwo(
getRequiredEnvParam("GITHUB_REPOSITORY"),
);
await getApiClient().rest.actions.deleteActionsCacheById({
owner: repositoryNwo.owner,
repo: repositoryNwo.repo,
key,
ref,
cache_id: id,
});
}

View File

@@ -1,7 +1,7 @@
import { existsSync, mkdirSync, writeFileSync } from "fs";
import path from "path";
import { Config } from "./config-utils";
import type { Config } from "./config-utils";
import { Language } from "./languages";
import { getActionsLogger } from "./logging";
import { getCodeQLDatabasePath } from "./util";

View File

@@ -7,6 +7,7 @@ import * as actionsUtil from "./actions-util";
import * as apiClient from "./api-client";
import { CodeQL } from "./codeql";
import type { Config } from "./config-utils";
import { addDiagnostic, makeDiagnostic } from "./diagnostics";
import { Language } from "./languages";
import { Logger } from "./logging";
import { tryGetFolderBytes, withTimeout } from "./util";
@@ -167,10 +168,45 @@ export async function cleanupTrapCaches(config: Config, logger: Logger) {
try {
for (const language of config.languages) {
if (config.trapCaches[language]) {
const matchingCaches = await getTrapCachesForLanguage(language, logger);
for (const cache of matchingCaches) {
logger.info(`Matched Actions cache ${JSON.stringify(cache)}`);
const cachesToRemove = await getTrapCachesForLanguage(language, logger);
// Dates returned by the API are in ISO 8601 format, so we can sort them lexicographically
cachesToRemove.sort((a, b) => b.created_at.localeCompare(a.created_at));
// Keep the most recent cache
logger.debug(
`Keeping newest TRAP cache (${JSON.stringify(cachesToRemove[0])})`,
);
cachesToRemove.pop();
for (const cache of cachesToRemove) {
logger.debug(`Deleting old TRAP cache (${JSON.stringify(cache)})`);
await apiClient.deleteActionsCache(cache.id);
}
const totalBytesCleanedUp = cachesToRemove.reduce(
(acc, item) => acc + item.size_in_bytes,
0,
);
const totalMegabytesCleanedUp = (
totalBytesCleanedUp /
(1024 * 1024)
).toFixed(2);
const message = `Cleaned up ${totalMegabytesCleanedUp} MiB of old TRAP caches for ${language}.`;
logger.info(message);
addDiagnostic(
config,
language,
makeDiagnostic(
"codeql-action/trap-caching/cleanup",
"TRAP caching cleanup statistics",
{
attributes: {
totalBytesCleanedUp,
},
plaintextMessage: message,
visibility: {
telemetry: true,
},
},
),
);
}
}
} catch (e) {
@@ -181,16 +217,25 @@ export async function cleanupTrapCaches(config: Config, logger: Logger) {
async function getTrapCachesForLanguage(
language: Language,
logger: Logger,
): Promise<apiClient.ActionsCacheItem[]> {
): Promise<Array<Required<apiClient.ActionsCacheItem>>> {
logger.debug(`Listing TRAP caches for ${language}`);
const allCaches = await apiClient.listActionsCaches(
CODEQL_TRAP_CACHE_PREFIX,
await actionsUtil.getRef(),
logger,
);
for (const cache of allCaches) {
if (!cache.created_at || !cache.id || !cache.key || !cache.size_in_bytes) {
throw new Error(
"An unexpected cache item was returned from the API that was missing one or " +
`more required fields: ${JSON.stringify(cache)}`,
);
}
}
return allCaches.filter((cache) => {
return cache.key?.includes(`-${language}-`);
});
}) as Array<Required<apiClient.ActionsCacheItem>>;
}
export async function getLanguagesSupportingCaching(