Skip to content

Commit ff625c8

Browse files
committed
Add bare-bones dependency caching functions
1 parent b0ae6a9 commit ff625c8

File tree

3 files changed

+271
-0
lines changed

3 files changed

+271
-0
lines changed

lib/dependency-caching.js

Lines changed: 125 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/dependency-caching.js.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/dependency-caching.ts

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import * as os from "os";
2+
import { join } from "path";
3+
4+
import * as actionsCache from "@actions/cache";
5+
import * as glob from "@actions/glob";
6+
7+
import { getTotalCacheSize } from "./caching-utils";
8+
import { Config } from "./config-utils";
9+
import { Language } from "./languages";
10+
import { Logger } from "./logging";
11+
import { getRequiredEnvParam } from "./util";
12+
13+
interface CacheConfig {
14+
paths: string[];
15+
hash: string[];
16+
}
17+
18+
const CODEQL_DEPENDENCY_CACHE_PREFIX = "codeql-dependencies";
19+
const CODEQL_DEPENDENCY_CACHE_VERSION = 1;
20+
21+
const CODEQL_DEFAULT_CACHE_CONFIG: { [language: string]: CacheConfig } = {
22+
java: {
23+
paths: [
24+
// Maven
25+
join(os.homedir(), ".m2", "repository"),
26+
// Gradle
27+
join(os.homedir(), ".gradle", "caches"),
28+
],
29+
hash: [
30+
// Maven
31+
"**/pom.xml",
32+
// Gradle
33+
"**/*.gradle*",
34+
"**/gradle-wrapper.properties",
35+
"buildSrc/**/Versions.kt",
36+
"buildSrc/**/Dependencies.kt",
37+
"gradle/*.versions.toml",
38+
"**/versions.properties",
39+
],
40+
},
41+
};
42+
43+
/**
44+
* Attempts to restore dependency caches for the languages being analyzed.
45+
*
46+
* @param languages The languages being analyzed.
47+
* @param logger A logger to record some informational messages to.
48+
* @returns A list of languages for which dependency caches were restored.
49+
*/
50+
export async function downloadDependencyCaches(
51+
languages: Language[],
52+
logger: Logger,
53+
): Promise<Language[]> {
54+
const restoredCaches: Language[] = [];
55+
56+
for (const language of languages) {
57+
const cacheConfig = CODEQL_DEFAULT_CACHE_CONFIG[language];
58+
59+
if (cacheConfig === undefined) {
60+
logger.info(
61+
`Skipping download of dependency cache for ${language} as we have no caching configuration for it.`,
62+
);
63+
continue;
64+
}
65+
66+
const primaryKey = await cacheKey(language, cacheConfig);
67+
const restoreKeys: string[] = [await cachePrefix(language)];
68+
69+
logger.info(
70+
`Downloading cache for ${language} with key ${primaryKey} and restore keys ${restoreKeys.join(
71+
", ",
72+
)}`,
73+
);
74+
75+
const hitKey = await actionsCache.restoreCache(
76+
cacheConfig.paths,
77+
primaryKey,
78+
restoreKeys,
79+
);
80+
81+
if (hitKey !== undefined) {
82+
logger.info(`Cache hit on key ${hitKey} for ${language}.`);
83+
restoredCaches.push(language);
84+
}
85+
}
86+
87+
return restoredCaches;
88+
}
89+
90+
/**
91+
* Attempts to store caches for the languages that were analyzed.
92+
*
93+
* @param config The configuration for this workflow.
94+
* @param logger A logger to record some informational messages to.
95+
*/
96+
export async function uploadDependencyCaches(config: Config, logger: Logger) {
97+
for (const language of config.languages) {
98+
const cacheConfig = CODEQL_DEFAULT_CACHE_CONFIG[language];
99+
100+
if (cacheConfig === undefined) {
101+
logger.info(
102+
`Skipping upload of dependency cache for ${language} as we have no caching configuration for it.`,
103+
);
104+
continue;
105+
}
106+
107+
const globber = await glob.create(cacheConfig.hash.join("\n"));
108+
const size = await getTotalCacheSize(await globber.glob(), logger);
109+
110+
const key = await cacheKey(language, cacheConfig);
111+
112+
logger.info(
113+
`Uploading cache of size ${size} for ${language} with key ${key}`,
114+
);
115+
116+
await actionsCache.saveCache(cacheConfig.paths, key);
117+
}
118+
}
119+
120+
/**
121+
* Computes a cache key for the specified language.
122+
*
123+
* @param language The language being analyzed.
124+
* @param cacheConfig The cache configuration for the language.
125+
* @returns A cache key capturing information about the project(s) being analyzed in the specified language.
126+
*/
127+
async function cacheKey(
128+
language: Language,
129+
cacheConfig: CacheConfig,
130+
): Promise<string> {
131+
const hash = await glob.hashFiles(cacheConfig.hash.join("\n"));
132+
return `${await cachePrefix(language)}${hash}`;
133+
}
134+
135+
/**
136+
* Constructs a prefix for the cache key, comprised of a CodeQL-specific prefix, a version number that
137+
* can be changed to invalidate old caches, the runner's operating system, and the specified language name.
138+
*
139+
* @param language The language being analyzed.
140+
* @returns The prefix that identifies what a cache is for.
141+
*/
142+
async function cachePrefix(language: Language): Promise<string> {
143+
const runnerOs = getRequiredEnvParam("RUNNER_OS");
144+
return `${CODEQL_DEPENDENCY_CACHE_PREFIX}-${CODEQL_DEPENDENCY_CACHE_VERSION}-${runnerOs}-${language}-`;
145+
}

0 commit comments

Comments
 (0)