|
1 | 1 | import*asfsfrom"fs";
|
2 | 2 | import*aspathfrom"path";
|
3 | 3 |
|
4 |
| -import{getTemporaryDirectory}from"./actions-util"; |
| 4 | +import*asactionsCachefrom"@actions/cache"; |
| 5 | + |
| 6 | +import{getRequiredInput,getTemporaryDirectory}from"./actions-util"; |
| 7 | +import{typeCodeQL}from"./codeql"; |
5 | 8 | import{typeConfig}from"./config-utils";
|
6 |
| -import{getFileOidsUnderPath}from"./git-utils"; |
| 9 | +import{getCommitOid,getFileOidsUnderPath}from"./git-utils"; |
7 | 10 | import{Logger}from"./logging";
|
| 11 | +import{isInTestMode,withTimeout}from"./util"; |
8 | 12 |
|
9 | 13 | exportenumOverlayDatabaseMode{
|
10 | 14 | Overlay="overlay",
|
@@ -122,3 +126,111 @@ function computeChangedFiles(
|
122 | 126 | }
|
123 | 127 | returnchanges;
|
124 | 128 | }
|
| 129 | + |
| 130 | +// Constants for database caching |
| 131 | +constCACHE_VERSION=1; |
| 132 | +constCACHE_PREFIX="codeql-overlay-base-database"; |
| 133 | +constMAX_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 | + * This function uses the `checkout_path` input to determine the repository path |
| 140 | + * and works only when called from `analyze` or `upload-sarif`. |
| 141 | + * |
| 142 | + *@param codeql The CodeQL instance |
| 143 | + *@param config The configuration object |
| 144 | + *@param logger The logger instance |
| 145 | + *@returns A promise that resolves to true if the upload was performed and |
| 146 | + * successfully completed, or false otherwise |
| 147 | + */ |
| 148 | +exportasyncfunctionuploadOverlayBaseDatabaseToCache( |
| 149 | +codeql:CodeQL, |
| 150 | +config:Config, |
| 151 | +logger:Logger, |
| 152 | +):Promise<boolean>{ |
| 153 | +constoverlayDatabaseMode=config.augmentationProperties.overlayDatabaseMode; |
| 154 | +if(overlayDatabaseMode!==OverlayDatabaseMode.OverlayBase){ |
| 155 | +logger.debug( |
| 156 | +`Overlay database mode is${overlayDatabaseMode}. `+ |
| 157 | +"Skip uploading overlay-base database to cache.", |
| 158 | +); |
| 159 | +returnfalse; |
| 160 | +} |
| 161 | +if(!config.augmentationProperties.useOverlayDatabaseCaching){ |
| 162 | +logger.debug( |
| 163 | +"Overlay database caching is disabled. "+ |
| 164 | +"Skip uploading overlay-base database to cache.", |
| 165 | +); |
| 166 | +returnfalse; |
| 167 | +} |
| 168 | +if(isInTestMode()){ |
| 169 | +logger.debug( |
| 170 | +"In test mode. Skip uploading overlay-base database to cache.", |
| 171 | +); |
| 172 | +returnfalse; |
| 173 | +} |
| 174 | + |
| 175 | +// An overlay-base database should contain the base database OIDs file. |
| 176 | +// Verifying that the file exists serves as a sanity check. |
| 177 | +constbaseDatabaseOidsFilePath=getBaseDatabaseOidsFilePath(config); |
| 178 | +if(!fs.existsSync(baseDatabaseOidsFilePath)){ |
| 179 | +logger.warning( |
| 180 | +"Cannot upload overlay-base database to cache: "+ |
| 181 | +`${baseDatabaseOidsFilePath} does not exist`, |
| 182 | +); |
| 183 | +returnfalse; |
| 184 | +} |
| 185 | + |
| 186 | +constdbLocation=config.dbLocation; |
| 187 | +constcodeQlVersion=(awaitcodeql.getVersion()).version; |
| 188 | +constcheckoutPath=getRequiredInput("checkout_path"); |
| 189 | +constcacheKey=awaitgenerateCacheKey(config,codeQlVersion,checkoutPath); |
| 190 | +logger.info( |
| 191 | +`Uploading overlay-base database to Actions cache with key${cacheKey}`, |
| 192 | +); |
| 193 | + |
| 194 | +try{ |
| 195 | +constcacheId=awaitwithTimeout( |
| 196 | +MAX_CACHE_OPERATION_MS, |
| 197 | +actionsCache.saveCache([dbLocation],cacheKey), |
| 198 | +()=>{}, |
| 199 | +); |
| 200 | +if(cacheId===undefined){ |
| 201 | +logger.warning("Timed out while uploading overlay-base database"); |
| 202 | +returnfalse; |
| 203 | +} |
| 204 | +}catch(error){ |
| 205 | +logger.warning( |
| 206 | +"Failed to upload overlay-base database to cache: "+ |
| 207 | +`${errorinstanceofError ?error.message :String(error)}`, |
| 208 | +); |
| 209 | +returnfalse; |
| 210 | +} |
| 211 | +logger.info(`Successfully uploaded overlay-base database from${dbLocation}`); |
| 212 | +returntrue; |
| 213 | +} |
| 214 | + |
| 215 | +asyncfunctiongenerateCacheKey( |
| 216 | +config:Config, |
| 217 | +codeQlVersion:string, |
| 218 | +checkoutPath:string, |
| 219 | +):Promise<string>{ |
| 220 | +constsha=awaitgetCommitOid(checkoutPath); |
| 221 | +return`${getCacheRestoreKey(config,codeQlVersion)}${sha}`; |
| 222 | +} |
| 223 | + |
| 224 | +functiongetCacheRestoreKey(config:Config,codeQlVersion:string):string{ |
| 225 | +// The restore key (prefix) specifies which cached overlay-base databases are |
| 226 | +// compatible with the current analysis: the cached database must have the |
| 227 | +// same cache version and the same CodeQL bundle version. |
| 228 | +// |
| 229 | +// Actions cache supports using multiple restore keys to indicate preference. |
| 230 | +// Technically we prefer a cached overlay-base database with the same SHA as |
| 231 | +// we are analyzing. However, since overlay-base databases are built from the |
| 232 | +// default branch and used in PR analysis, it is exceedingly unlikely that |
| 233 | +// the commit SHA will ever be the same, so we can just leave it out. |
| 234 | +constlanguages=[...config.languages].sort().join("_"); |
| 235 | +return`${CACHE_PREFIX}-${CACHE_VERSION}-${languages}-${codeQlVersion}-`; |
| 236 | +} |