Skip to content

Commit 1b32ed3

Browse files
committed
Add getCodeQLVersionFromOverlayBaseDatabase()
1 parent ef45800 commit 1b32ed3

File tree

2 files changed

+204
-1
lines changed

2 files changed

+204
-1
lines changed

src/overlay-database-utils.test.ts

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
getCacheRestoreKeyPrefix,
1515
getCacheSaveKey,
1616
getCacheWorkflowKeyPrefix,
17+
getCodeQLVersionFromOverlayBaseDatabase,
1718
OverlayDatabaseMode,
1819
writeBaseDatabaseOidsFile,
1920
writeOverlayChangesFile,
@@ -315,3 +316,146 @@ test("overlay-base database cache keys remain stable", async (t) => {
315316
`Expected restore key prefix "${restoreKeyPrefix}" to start with workflow key prefix "${workflowKeyPrefix}"`,
316317
);
317318
});
319+
320+
/**
321+
* Helper function to generate a cache save key for testing.
322+
* Sets up the necessary sinon stubs and returns the generated cache key.
323+
*/
324+
async function generateTestCacheKey(codeQlVersion: string): Promise<string> {
325+
const config = createTestConfig({ languages: ["python", "javascript"] });
326+
const commitOid = "abc123def456";
327+
328+
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
329+
sinon.stub(gitUtils, "getCommitOid").resolves(commitOid);
330+
331+
return await getCacheSaveKey(config, codeQlVersion, "checkout-path");
332+
}
333+
334+
/**
335+
* Helper function to stub getMostRecentActionsCacheEntry with a given key and creation date.
336+
* Returns the stubbed function for cleanup if needed.
337+
*/
338+
function stubMostRecentActionsCacheEntry(key?: string, createdAt?: Date) {
339+
const cacheItem =
340+
key !== undefined || createdAt !== undefined
341+
? {
342+
key,
343+
created_at: createdAt?.toISOString(),
344+
}
345+
: undefined;
346+
347+
return sinon
348+
.stub(apiClient, "getMostRecentActionsCacheEntry")
349+
.resolves(cacheItem);
350+
}
351+
352+
test("getCodeQLVersionFromOverlayBaseDatabase returns version when cache entry is valid", async (t) => {
353+
const logger = getRunnerLogger(true);
354+
const cacheKey = await generateTestCacheKey("2.23.0");
355+
356+
stubMostRecentActionsCacheEntry(cacheKey, new Date());
357+
358+
const result = await getCodeQLVersionFromOverlayBaseDatabase(logger);
359+
t.is(result, "2.23.0", "Should return the extracted CodeQL version");
360+
});
361+
362+
test("getCodeQLVersionFromOverlayBaseDatabase returns undefined when no cache entries found", async (t) => {
363+
const logger = getRunnerLogger(true);
364+
365+
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
366+
stubMostRecentActionsCacheEntry();
367+
368+
const result = await getCodeQLVersionFromOverlayBaseDatabase(logger);
369+
t.is(
370+
result,
371+
undefined,
372+
"Should return undefined when no cache entries found",
373+
);
374+
});
375+
376+
test("getCodeQLVersionFromOverlayBaseDatabase returns undefined when cache entry is too old", async (t) => {
377+
const logger = getRunnerLogger(true);
378+
const cacheKey = await generateTestCacheKey("2.23.0");
379+
380+
const oldDate = new Date();
381+
oldDate.setDate(oldDate.getDate() - 15); // 15 days ago (older than 14 day limit)
382+
383+
stubMostRecentActionsCacheEntry(cacheKey, oldDate);
384+
385+
const result = await getCodeQLVersionFromOverlayBaseDatabase(logger);
386+
t.is(
387+
result,
388+
undefined,
389+
"Should return undefined when cache entry is too old",
390+
);
391+
});
392+
393+
test("getCodeQLVersionFromOverlayBaseDatabase returns undefined when cache key format is invalid", async (t) => {
394+
const logger = getRunnerLogger(true);
395+
396+
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
397+
stubMostRecentActionsCacheEntry("invalid-key-format", new Date());
398+
399+
const result = await getCodeQLVersionFromOverlayBaseDatabase(logger);
400+
t.is(
401+
result,
402+
undefined,
403+
"Should return undefined when cache key format is invalid",
404+
);
405+
});
406+
407+
test("getCodeQLVersionFromOverlayBaseDatabase returns undefined when CodeQL version is invalid semver", async (t) => {
408+
const logger = getRunnerLogger(true);
409+
const invalidCacheKey = await generateTestCacheKey("invalid.version");
410+
411+
stubMostRecentActionsCacheEntry(invalidCacheKey, new Date());
412+
413+
const result = await getCodeQLVersionFromOverlayBaseDatabase(logger);
414+
t.is(
415+
result,
416+
undefined,
417+
"Should return undefined when CodeQL version is invalid semver",
418+
);
419+
});
420+
421+
test("getCodeQLVersionFromOverlayBaseDatabase returns undefined when CodeQL version is too old", async (t) => {
422+
const logger = getRunnerLogger(true);
423+
const cacheKey = await generateTestCacheKey("2.20.0"); // Older than minimum required version (2.22.4)
424+
425+
stubMostRecentActionsCacheEntry(cacheKey, new Date());
426+
427+
const result = await getCodeQLVersionFromOverlayBaseDatabase(logger);
428+
t.is(
429+
result,
430+
undefined,
431+
"Should return undefined when CodeQL version is older than minimum required version",
432+
);
433+
});
434+
435+
test("getCodeQLVersionFromOverlayBaseDatabase returns undefined when cache entry has no key", async (t) => {
436+
const logger = getRunnerLogger(true);
437+
438+
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
439+
stubMostRecentActionsCacheEntry(undefined, new Date());
440+
441+
const result = await getCodeQLVersionFromOverlayBaseDatabase(logger);
442+
t.is(
443+
result,
444+
undefined,
445+
"Should return undefined when cache entry has no key",
446+
);
447+
});
448+
449+
test("getCodeQLVersionFromOverlayBaseDatabase returns undefined when cache entry has no created_at", async (t) => {
450+
const logger = getRunnerLogger(true);
451+
const cacheKey = await generateTestCacheKey("2.23.0");
452+
453+
stubMostRecentActionsCacheEntry(cacheKey, undefined);
454+
455+
const result = await getCodeQLVersionFromOverlayBaseDatabase(logger);
456+
t.is(
457+
result,
458+
undefined,
459+
"Should return undefined when cache entry has no created_at",
460+
);
461+
});

src/overlay-database-utils.ts

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import * as fs from "fs";
33
import * as path from "path";
44

55
import * as actionsCache from "@actions/cache";
6+
import * as semver from "semver";
67

78
import { getRequiredInput, getTemporaryDirectory } from "./actions-util";
8-
import { getAutomationID } from "./api-client";
9+
import { getAutomationID, getMostRecentActionsCacheEntry } from "./api-client";
910
import { type CodeQL } from "./codeql";
1011
import { type Config } from "./config-utils";
1112
import { getCommitOid, getFileOidsUnderPath } from "./git-utils";
@@ -441,6 +442,64 @@ export async function downloadOverlayBaseDatabaseFromCache(
441442
};
442443
}
443444

445+
const IGNORE_DATABASES_OLDER_THAN_N_DAYS = 14;
446+
447+
export async function getCodeQLVersionFromOverlayBaseDatabase(
448+
logger: Logger,
449+
): Promise<string | undefined> {
450+
const keyPrefix = await getCacheWorkflowKeyPrefix();
451+
const cacheItem = await getMostRecentActionsCacheEntry(keyPrefix);
452+
453+
if (cacheItem?.created_at === undefined || cacheItem.key === undefined) {
454+
logger.info("No overlay-base database cache entries found");
455+
return undefined;
456+
}
457+
458+
const cutoffTime = new Date();
459+
cutoffTime.setDate(cutoffTime.getDate() - IGNORE_DATABASES_OLDER_THAN_N_DAYS);
460+
461+
const cacheCreationTime = new Date(cacheItem.created_at);
462+
if (cacheCreationTime < cutoffTime) {
463+
logger.info(
464+
`Not considering overlay-base database cache entry ${cacheItem.key} ` +
465+
`because it is too old (created at ${cacheItem.created_at})`,
466+
);
467+
return undefined;
468+
}
469+
470+
const keyParts = cacheItem.key.split("-");
471+
if (keyParts.length < 9) {
472+
logger.info(
473+
`Overlay-base database cache entry ${cacheItem.key} has invalid key format`,
474+
);
475+
return undefined;
476+
}
477+
const codeQlVersion = keyParts[keyParts.length - 2];
478+
479+
if (!semver.valid(codeQlVersion)) {
480+
logger.info(
481+
`Overlay-base database cache entry ${cacheItem.key} has invalid ` +
482+
`CodeQL version ${codeQlVersion}`,
483+
);
484+
return undefined;
485+
}
486+
487+
if (semver.lt(codeQlVersion, CODEQL_OVERLAY_MINIMUM_VERSION)) {
488+
logger.info(
489+
`Overlay-base database cache entry ${cacheItem.key} has ` +
490+
`CodeQL version ${codeQlVersion}, which is older than the ` +
491+
`minimum required version ${CODEQL_OVERLAY_MINIMUM_VERSION}`,
492+
);
493+
return undefined;
494+
}
495+
496+
logger.info(
497+
`Found overlay-base database cache entry ${cacheItem.key} ` +
498+
`created at ${cacheItem.created_at} with CodeQL version ${codeQlVersion}`,
499+
);
500+
return codeQlVersion;
501+
}
502+
444503
/**
445504
* Computes the cache key for saving the overlay-base database to the GitHub
446505
* Actions cache.

0 commit comments

Comments
 (0)