mirror of
https://github.com/github/codeql-action.git
synced 2026-11-11 15:25:34 +00:00
Add getCodeQLVersionFromOverlayBaseDatabase()
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
getCacheRestoreKeyPrefix,
|
||||
getCacheSaveKey,
|
||||
getCacheWorkflowKeyPrefix,
|
||||
getCodeQLVersionFromOverlayBaseDatabase,
|
||||
OverlayDatabaseMode,
|
||||
writeBaseDatabaseOidsFile,
|
||||
writeOverlayChangesFile,
|
||||
@@ -315,3 +316,146 @@ test("overlay-base database cache keys remain stable", async (t) => {
|
||||
`Expected restore key prefix "${restoreKeyPrefix}" to start with workflow key prefix "${workflowKeyPrefix}"`,
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Helper function to generate a cache save key for testing.
|
||||
* Sets up the necessary sinon stubs and returns the generated cache key.
|
||||
*/
|
||||
async function generateTestCacheKey(codeQlVersion: string): Promise<string> {
|
||||
const config = createTestConfig({ languages: ["python", "javascript"] });
|
||||
const commitOid = "abc123def456";
|
||||
|
||||
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
|
||||
sinon.stub(gitUtils, "getCommitOid").resolves(commitOid);
|
||||
|
||||
return await getCacheSaveKey(config, codeQlVersion, "checkout-path");
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to stub getMostRecentActionsCacheEntry with a given key and creation date.
|
||||
* Returns the stubbed function for cleanup if needed.
|
||||
*/
|
||||
function stubMostRecentActionsCacheEntry(key?: string, createdAt?: Date) {
|
||||
const cacheItem =
|
||||
key !== undefined || createdAt !== undefined
|
||||
? {
|
||||
key,
|
||||
created_at: createdAt?.toISOString(),
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return sinon
|
||||
.stub(apiClient, "getMostRecentActionsCacheEntry")
|
||||
.resolves(cacheItem);
|
||||
}
|
||||
|
||||
test("getCodeQLVersionFromOverlayBaseDatabase returns version when cache entry is valid", async (t) => {
|
||||
const logger = getRunnerLogger(true);
|
||||
const cacheKey = await generateTestCacheKey("2.23.0");
|
||||
|
||||
stubMostRecentActionsCacheEntry(cacheKey, new Date());
|
||||
|
||||
const result = await getCodeQLVersionFromOverlayBaseDatabase(logger);
|
||||
t.is(result, "2.23.0", "Should return the extracted CodeQL version");
|
||||
});
|
||||
|
||||
test("getCodeQLVersionFromOverlayBaseDatabase returns undefined when no cache entries found", async (t) => {
|
||||
const logger = getRunnerLogger(true);
|
||||
|
||||
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
|
||||
stubMostRecentActionsCacheEntry();
|
||||
|
||||
const result = await getCodeQLVersionFromOverlayBaseDatabase(logger);
|
||||
t.is(
|
||||
result,
|
||||
undefined,
|
||||
"Should return undefined when no cache entries found",
|
||||
);
|
||||
});
|
||||
|
||||
test("getCodeQLVersionFromOverlayBaseDatabase returns undefined when cache entry is too old", async (t) => {
|
||||
const logger = getRunnerLogger(true);
|
||||
const cacheKey = await generateTestCacheKey("2.23.0");
|
||||
|
||||
const oldDate = new Date();
|
||||
oldDate.setDate(oldDate.getDate() - 15); // 15 days ago (older than 14 day limit)
|
||||
|
||||
stubMostRecentActionsCacheEntry(cacheKey, oldDate);
|
||||
|
||||
const result = await getCodeQLVersionFromOverlayBaseDatabase(logger);
|
||||
t.is(
|
||||
result,
|
||||
undefined,
|
||||
"Should return undefined when cache entry is too old",
|
||||
);
|
||||
});
|
||||
|
||||
test("getCodeQLVersionFromOverlayBaseDatabase returns undefined when cache key format is invalid", async (t) => {
|
||||
const logger = getRunnerLogger(true);
|
||||
|
||||
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
|
||||
stubMostRecentActionsCacheEntry("invalid-key-format", new Date());
|
||||
|
||||
const result = await getCodeQLVersionFromOverlayBaseDatabase(logger);
|
||||
t.is(
|
||||
result,
|
||||
undefined,
|
||||
"Should return undefined when cache key format is invalid",
|
||||
);
|
||||
});
|
||||
|
||||
test("getCodeQLVersionFromOverlayBaseDatabase returns undefined when CodeQL version is invalid semver", async (t) => {
|
||||
const logger = getRunnerLogger(true);
|
||||
const invalidCacheKey = await generateTestCacheKey("invalid.version");
|
||||
|
||||
stubMostRecentActionsCacheEntry(invalidCacheKey, new Date());
|
||||
|
||||
const result = await getCodeQLVersionFromOverlayBaseDatabase(logger);
|
||||
t.is(
|
||||
result,
|
||||
undefined,
|
||||
"Should return undefined when CodeQL version is invalid semver",
|
||||
);
|
||||
});
|
||||
|
||||
test("getCodeQLVersionFromOverlayBaseDatabase returns undefined when CodeQL version is too old", async (t) => {
|
||||
const logger = getRunnerLogger(true);
|
||||
const cacheKey = await generateTestCacheKey("2.20.0"); // Older than minimum required version (2.22.4)
|
||||
|
||||
stubMostRecentActionsCacheEntry(cacheKey, new Date());
|
||||
|
||||
const result = await getCodeQLVersionFromOverlayBaseDatabase(logger);
|
||||
t.is(
|
||||
result,
|
||||
undefined,
|
||||
"Should return undefined when CodeQL version is older than minimum required version",
|
||||
);
|
||||
});
|
||||
|
||||
test("getCodeQLVersionFromOverlayBaseDatabase returns undefined when cache entry has no key", async (t) => {
|
||||
const logger = getRunnerLogger(true);
|
||||
|
||||
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
|
||||
stubMostRecentActionsCacheEntry(undefined, new Date());
|
||||
|
||||
const result = await getCodeQLVersionFromOverlayBaseDatabase(logger);
|
||||
t.is(
|
||||
result,
|
||||
undefined,
|
||||
"Should return undefined when cache entry has no key",
|
||||
);
|
||||
});
|
||||
|
||||
test("getCodeQLVersionFromOverlayBaseDatabase returns undefined when cache entry has no created_at", async (t) => {
|
||||
const logger = getRunnerLogger(true);
|
||||
const cacheKey = await generateTestCacheKey("2.23.0");
|
||||
|
||||
stubMostRecentActionsCacheEntry(cacheKey, undefined);
|
||||
|
||||
const result = await getCodeQLVersionFromOverlayBaseDatabase(logger);
|
||||
t.is(
|
||||
result,
|
||||
undefined,
|
||||
"Should return undefined when cache entry has no created_at",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -3,9 +3,10 @@ import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
import * as actionsCache from "@actions/cache";
|
||||
import * as semver from "semver";
|
||||
|
||||
import { getRequiredInput, getTemporaryDirectory } from "./actions-util";
|
||||
import { getAutomationID } from "./api-client";
|
||||
import { getAutomationID, getMostRecentActionsCacheEntry } from "./api-client";
|
||||
import { type CodeQL } from "./codeql";
|
||||
import { type Config } from "./config-utils";
|
||||
import { getCommitOid, getFileOidsUnderPath } from "./git-utils";
|
||||
@@ -441,6 +442,64 @@ export async function downloadOverlayBaseDatabaseFromCache(
|
||||
};
|
||||
}
|
||||
|
||||
const IGNORE_DATABASES_OLDER_THAN_N_DAYS = 14;
|
||||
|
||||
export async function getCodeQLVersionFromOverlayBaseDatabase(
|
||||
logger: Logger,
|
||||
): Promise<string | undefined> {
|
||||
const keyPrefix = await getCacheWorkflowKeyPrefix();
|
||||
const cacheItem = await getMostRecentActionsCacheEntry(keyPrefix);
|
||||
|
||||
if (cacheItem?.created_at === undefined || cacheItem.key === undefined) {
|
||||
logger.info("No overlay-base database cache entries found");
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const cutoffTime = new Date();
|
||||
cutoffTime.setDate(cutoffTime.getDate() - IGNORE_DATABASES_OLDER_THAN_N_DAYS);
|
||||
|
||||
const cacheCreationTime = new Date(cacheItem.created_at);
|
||||
if (cacheCreationTime < cutoffTime) {
|
||||
logger.info(
|
||||
`Not considering overlay-base database cache entry ${cacheItem.key} ` +
|
||||
`because it is too old (created at ${cacheItem.created_at})`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const keyParts = cacheItem.key.split("-");
|
||||
if (keyParts.length < 9) {
|
||||
logger.info(
|
||||
`Overlay-base database cache entry ${cacheItem.key} has invalid key format`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
const codeQlVersion = keyParts[keyParts.length - 2];
|
||||
|
||||
if (!semver.valid(codeQlVersion)) {
|
||||
logger.info(
|
||||
`Overlay-base database cache entry ${cacheItem.key} has invalid ` +
|
||||
`CodeQL version ${codeQlVersion}`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (semver.lt(codeQlVersion, CODEQL_OVERLAY_MINIMUM_VERSION)) {
|
||||
logger.info(
|
||||
`Overlay-base database cache entry ${cacheItem.key} has ` +
|
||||
`CodeQL version ${codeQlVersion}, which is older than the ` +
|
||||
`minimum required version ${CODEQL_OVERLAY_MINIMUM_VERSION}`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`Found overlay-base database cache entry ${cacheItem.key} ` +
|
||||
`created at ${cacheItem.created_at} with CodeQL version ${codeQlVersion}`,
|
||||
);
|
||||
return codeQlVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the cache key for saving the overlay-base database to the GitHub
|
||||
* Actions cache.
|
||||
|
||||
Reference in New Issue
Block a user