Skip to content

Basic support for overlay PR analysis #2945

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Jul 7, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Upload overlay-base database to actions cache
  • Loading branch information
cklin committed Jul 3, 2025
commit 6ca06f41c4d79aaf9fbff552e26cdac3f12a80e7
4 changes: 4 additions & 0 deletions src/analyze-action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { EnvVar } from "./environment";
import { Features } from "./feature-flags";
import { Language } from "./languages";
import { getActionsLogger, Logger } from "./logging";
import { uploadOverlayBaseDatabaseToCache } from "./overlay-database-utils";
import { getRepositoryNwo } from "./repository";
import * as statusReport from "./status-report";
import {
Expand Down Expand Up @@ -349,6 +350,9 @@ async function run() {
// Possibly upload the database bundles for remote queries
await uploadDatabases(repositoryNwo, config, apiDetails, logger);

// Possibly upload the overlay-base database to actions cache
await uploadOverlayBaseDatabaseToCache(codeql, config, logger);

// Possibly upload the TRAP caches for later re-use
const trapCacheUploadStartTime = performance.now();
didUploadTrapCaches = await uploadTrapCaches(codeql, config, logger);
Expand Down
116 changes: 114 additions & 2 deletions src/overlay-database-utils.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import * as fs from "fs";
import * as path from "path";

import { getTemporaryDirectory } from "./actions-util";
import * as actionsCache from "@actions/cache";

import { getRequiredInput, getTemporaryDirectory } from "./actions-util";
import { type CodeQL } from "./codeql";
import { type Config } from "./config-utils";
import { getFileOidsUnderPath } from "./git-utils";
import { getCommitOid, getFileOidsUnderPath } from "./git-utils";
import { Logger } from "./logging";
import { isInTestMode, withTimeout } from "./util";

export enum OverlayDatabaseMode {
Overlay = "overlay",
Expand Down Expand Up @@ -122,3 +126,111 @@ function computeChangedFiles(
}
return changes;
}

// Constants for database caching
const CACHE_VERSION = 1;
const CACHE_PREFIX = "codeql-overlay-base-database";
const MAX_CACHE_OPERATION_MS = 120_000; // Two minutes

/**
* Uploads the overlay-base database to the GitHub Actions cache. If conditions
* for uploading are not met, the function does nothing and returns false.
*
* This function uses the `checkout_path` input to determine the repository path
* and works only when called from `analyze` or `upload-sarif`.
*
* @param codeql The CodeQL instance
* @param config The configuration object
* @param logger The logger instance
* @returns A promise that resolves to true if the upload was performed and
* successfully completed, or false otherwise
*/
export async function uploadOverlayBaseDatabaseToCache(
codeql: CodeQL,
config: Config,
logger: Logger,
): Promise<boolean> {
const overlayDatabaseMode = config.augmentationProperties.overlayDatabaseMode;
if (overlayDatabaseMode !== OverlayDatabaseMode.OverlayBase) {
Copy link
Preview

Copilot AI Jun 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The guard logic for overlay database caching (checking mode, caching flag, and test mode) is duplicated in both upload and download functions. Consider extracting this into a shared helper to reduce duplication and simplify future maintenance.

Copilot uses AI. Check for mistakes.

logger.debug(
`Overlay database mode is ${overlayDatabaseMode}. ` +
"Skip uploading overlay-base database to cache.",
);
return false;
}
if (!config.augmentationProperties.useOverlayDatabaseCaching) {
logger.debug(
"Overlay database caching is disabled. " +
"Skip uploading overlay-base database to cache.",
);
return false;
}
if (isInTestMode()) {
logger.debug(
"In test mode. Skip uploading overlay-base database to cache.",
);
return false;
}

// An overlay-base database should contain the base database OIDs file.
// Verifying that the file exists serves as a sanity check.
const baseDatabaseOidsFilePath = getBaseDatabaseOidsFilePath(config);
if (!fs.existsSync(baseDatabaseOidsFilePath)) {
logger.warning(
"Cannot upload overlay-base database to cache: " +
`${baseDatabaseOidsFilePath} does not exist`,
);
return false;
}

const dbLocation = config.dbLocation;
const codeQlVersion = (await codeql.getVersion()).version;
const checkoutPath = getRequiredInput("checkout_path");
const cacheKey = await generateCacheKey(config, codeQlVersion, checkoutPath);
logger.info(
`Uploading overlay-base database to Actions cache with key ${cacheKey}`,
);

try {
const cacheId = await withTimeout(
MAX_CACHE_OPERATION_MS,
actionsCache.saveCache([dbLocation], cacheKey),
() => {},
);
if (cacheId === undefined) {
logger.warning("Timed out while uploading overlay-base database");
return false;
}
} catch (error) {
logger.warning(
"Failed to upload overlay-base database to cache: " +
`${error instanceof Error ? error.message : String(error)}`,
);
return false;
}
logger.info(`Successfully uploaded overlay-base database from ${dbLocation}`);
return true;
}

async function generateCacheKey(
config: Config,
codeQlVersion: string,
checkoutPath: string,
): Promise<string> {
const sha = await getCommitOid(checkoutPath);
return `${getCacheRestoreKey(config, codeQlVersion)}${sha}`;
}

function getCacheRestoreKey(config: Config, codeQlVersion: string): string {
// The restore key (prefix) specifies which cached overlay-base databases are
// compatible with the current analysis: the cached database must have the
// same cache version and the same CodeQL bundle version.
//
// Actions cache supports using multiple restore keys to indicate preference.
// Technically we prefer a cached overlay-base database with the same SHA as
// we are analyzing. However, since overlay-base databases are built from the
// default branch and used in PR analysis, it is exceedingly unlikely that
// the commit SHA will ever be the same, so we can just leave it out.
const languages = [...config.languages].sort().join("_");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Henry has an open PR to open up languages more, which might affect this in the future. Not necessarily something we need to consider here, but may need to consider there.

return `${CACHE_PREFIX}-${CACHE_VERSION}-${languages}-${codeQlVersion}-`;
}
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy