Skip to content

Commit 89f5989

Browse files
committed
Upload overlay-base database to actions cache
1 parent 0fa6583 commit 89f5989

File tree

2 files changed

+108
-0
lines changed

2 files changed

+108
-0
lines changed

src/analyze-action.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { EnvVar } from "./environment";
2727
import { Features } from "./feature-flags";
2828
import { Language } from "./languages";
2929
import { getActionsLogger, Logger } from "./logging";
30+
import { uploadOverlayBaseDatabaseToCache } from "./overlay-database-utils";
3031
import { getRepositoryNwo } from "./repository";
3132
import * as statusReport from "./status-report";
3233
import {
@@ -336,6 +337,9 @@ async function run() {
336337
// Possibly upload the database bundles for remote queries
337338
await uploadDatabases(repositoryNwo, config, apiDetails, logger);
338339

340+
// Possibly upload the overlay-base database to actions cache
341+
await uploadOverlayBaseDatabaseToCache(codeql, config, logger);
342+
339343
// Possibly upload the TRAP caches for later re-use
340344
const trapCacheUploadStartTime = performance.now();
341345
didUploadTrapCaches = await uploadTrapCaches(codeql, config, logger);

src/overlay-database-utils.ts

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import * as fs from "fs";
22
import * as path from "path";
33

4+
import * as actionsCache from "@actions/cache";
5+
46
import { getTemporaryDirectory } from "./actions-util";
7+
import { type CodeQL } from "./codeql";
58
import { type Config } from "./config-utils";
69
import { getFileOidsUnderPath } from "./git-utils";
710
import { Logger } from "./logging";
11+
import { isInTestMode, withTimeout } from "./util";
812

913
export enum OverlayDatabaseMode {
1014
Overlay = "overlay",
@@ -122,3 +126,103 @@ function computeChangedFiles(
122126
}
123127
return changes;
124128
}
129+
130+
// Constants for database caching
131+
const CACHE_VERSION = 1;
132+
const CACHE_PREFIX = "codeql-overlay-base-database";
133+
const MAX_CACHE_OPERATION_MS = 120_000; // Two minutes
134+
135+
/**
136+
* Uploads the overlay-base database to the GitHub Actions cache. If conditions
137+
* for uploading are not met, the function does nothing and returns false.
138+
*
139+
* @param codeql The CodeQL instance
140+
* @param config The configuration object
141+
* @param logger The logger instance
142+
* @returns A promise that resolves to true if the upload was performed and
143+
* successfully completed, or false otherwise
144+
*/
145+
export async function uploadOverlayBaseDatabaseToCache(
146+
codeql: CodeQL,
147+
config: Config,
148+
logger: Logger,
149+
): Promise<boolean> {
150+
const overlayDatabaseMode = config.augmentationProperties.overlayDatabaseMode;
151+
if (overlayDatabaseMode !== OverlayDatabaseMode.OverlayBase) {
152+
logger.debug(
153+
`Overlay database mode is ${overlayDatabaseMode}. ` +
154+
"Skip uploading overlay-base database to cache.",
155+
);
156+
return false;
157+
}
158+
if (!config.augmentationProperties.useOverlayDatabaseCaching) {
159+
logger.debug(
160+
"Overlay database caching is disabled. " +
161+
"Skip uploading overlay-base database to cache.",
162+
);
163+
return false;
164+
}
165+
if (isInTestMode()) {
166+
logger.debug(
167+
"In test mode. Skip uploading overlay-base database to cache.",
168+
);
169+
return false;
170+
}
171+
172+
// An overlay-base database should contain the base database OIDs file.
173+
// Verifying that the file exists serves as a sanity check.
174+
const baseDatabaseOidsFilePath = getBaseDatabaseOidsFilePath(config);
175+
if (!fs.existsSync(baseDatabaseOidsFilePath)) {
176+
logger.warning(
177+
"Cannot upload overlay-base database to cache: " +
178+
`${baseDatabaseOidsFilePath} does not exist`,
179+
);
180+
return false;
181+
}
182+
183+
const dbLocation = config.dbLocation;
184+
const codeQlVersion = (await codeql.getVersion()).version;
185+
const cacheKey = generateCacheKey(config, codeQlVersion);
186+
logger.info(
187+
`Uploading overlay-base database to Actions cache with key ${cacheKey}`,
188+
);
189+
190+
try {
191+
const cacheId = await withTimeout(
192+
MAX_CACHE_OPERATION_MS,
193+
actionsCache.saveCache([dbLocation], cacheKey),
194+
() => {},
195+
);
196+
if (cacheId === undefined) {
197+
logger.warning("Timed out while uploading overlay-base database");
198+
return false;
199+
}
200+
} catch (error) {
201+
logger.warning(
202+
"Failed to upload overlay-base database to cache: " +
203+
`${error instanceof Error ? error.message : String(error)}`,
204+
);
205+
return false;
206+
}
207+
logger.info(`Successfully uploaded overlay-base database from ${dbLocation}`);
208+
return true;
209+
}
210+
211+
function generateCacheKey(config: Config, codeQlVersion: string): string {
212+
const sha = process.env.GITHUB_SHA || "unknown";
213+
return `${getCacheRestoreKey(config, codeQlVersion)}${sha}`;
214+
}
215+
216+
function getCacheRestoreKey(config: Config, codeQlVersion: string): string {
217+
// The restore key (prefix) specifies which cached overlay-base databases are
218+
// compatible with the current analysis: the cached database must have the
219+
// same cache version and the same CodeQL bundle version.
220+
//
221+
// Actions cache supports using multiple restore keys to indicate preference.
222+
// Technically we prefer a cached overlay-base database with the same SHA as
223+
// we are analyzing. However, since overlay-base databases are built from the
224+
// default branch and used in PR analysis, it is exceedingly unlikely that
225+
// the commit SHA will ever be the same, so we can just leave it out.
226+
const languages = [...config.languages].sort().join("_");
227+
return `${CACHE_PREFIX}-${CACHE_VERSION}-${languages}-${codeQlVersion}-`;
228+
}

0 commit comments

Comments
 (0)