name: Cache query compilation description: Caches CodeQL compilation caches - should be run both on PRs and pushes to main. inputs: key: description: 'The cache key to use - should be unique to the workflow' required: true outputs: cache-dir: description: "The directory where the cache was stored" value: ${{ steps.output-compilation-dir.outputs.compdir }} runs: using: composite steps: # calculate the merge-base with main, in a way that works both on PRs and pushes to main. - name: Calculate merge-base shell: bash if: ${{ github.event_name == 'pull_request' }} env: BASE_BRANCH: ${{ github.base_ref }} run: | MERGE_BASE=$(git cat-file commit $GITHUB_SHA | grep '^parent ' | head -1 | cut -f 2 -d " ") echo "merge_base=$MERGE_BASE" >> $GITHUB_ENV - name: Restore cache (PR) if: ${{ github.event_name == 'pull_request' }} uses: actions/cache/restore@v3 with: path: | **/.cache ~/.codeql/compile-cache key: codeql-compile-${{ inputs.key }}-pr-${{ github.sha }} restore-keys: | codeql-compile-${{ inputs.key }}-${{ github.base_ref }}-${{ env.merge_base }} codeql-compile-${{ inputs.key }}-${{ github.base_ref }}- codeql-compile-${{ inputs.key }}-main- - name: Fill cache (only branch push) if: ${{ github.event_name != 'pull_request' }} uses: actions/cache@v3 with: path: | **/.cache ~/.codeql/compile-cache key: codeql-compile-${{ inputs.key }}-${{ github.ref_name }}-${{ github.sha }} # just fill on main restore-keys: | # restore the latest cache if the exact cache is unavailable, to speed up compilation. codeql-compile-${{ inputs.key }}-${{ github.ref_name }}- codeql-compile-${{ inputs.key }}-main- - name: Output-compilationdir id: output-compilation-dir shell: bash run: | echo "compdir=${COMBINED_CACHE_DIR}" >> $GITHUB_OUTPUT env: COMBINED_CACHE_DIR: ${{ runner.temp }}/compilation-dir - name: Fill compilation cache directory id: fill-compilation-dir uses: actions/github-script@v6 env: COMBINED_CACHE_DIR: ${{ runner.temp }}/compilation-dir with: script: | // # Move all the existing cache into another folder, so we only preserve the cache for the current queries. // mkdir -p ${COMBINED_CACHE_DIR} // rm -f **/.cache/{lock,size} # -f to avoid errors if the cache is empty. // # copy the contents of the .cache folders into the combined cache folder. // cp -r **/.cache/* ${COMBINED_CACHE_DIR}/ || : # ignore missing files // # clean up the .cache folders // rm -rf **/.cache/* const fs = require("fs"); const path = require("path"); const os = require("os"); // the first argv is the cache folder to create. const COMBINED_CACHE_DIR = process.env.COMBINED_CACHE_DIR; function* walkCaches(dir) { const files = fs.readdirSync(dir, { withFileTypes: true }); for (const file of files) { if (file.isDirectory()) { const filePath = path.join(dir, file.name); yield* walkCaches(filePath); if (file.name === ".cache") { yield filePath; } } } } async function copyDir(src, dest) { for await (const file of await fs.promises.readdir(src, { withFileTypes: true })) { const srcPath = path.join(src, file.name); const destPath = path.join(dest, file.name); if (file.isDirectory()) { if (!fs.existsSync(destPath)) { fs.mkdirSync(destPath); } await copyDir(srcPath, destPath); } else { await fs.promises.copyFile(srcPath, destPath); } } } async function main() { const cacheDirs = [...walkCaches(".")]; for (const dir of cacheDirs) { console.log(`Found .cache dir at ${dir}`); } const globalCacheDir = path.join(os.homedir(), ".codeql", "compile-cache"); if (fs.existsSync(globalCacheDir)) { console.log("Found global home dir: " + globalCacheDir); cacheDirs.push(globalCacheDir); } if (cacheDirs.length === 0) { console.log("No cache dirs found"); return; } // mkdir -p ${COMBINED_CACHE_DIR} fs.mkdirSync(COMBINED_CACHE_DIR, { recursive: true }); // rm -f **/.cache/{lock,size} # -f to avoid errors if the cache is empty. await Promise.all( cacheDirs.map((cacheDir) => (async function () { await fs.promises.rm(path.join(cacheDir, "lock"), { force: true }); await fs.promises.rm(path.join(cacheDir, "size"), { force: true }); })() ) ); // # copy the contents of the .cache folders into the combined cache folder. // cp -r **/.cache/* ${COMBINED_CACHE_DIR}/ || : # ignore missing files await Promise.all( cacheDirs.map((cacheDir) => copyDir(cacheDir, COMBINED_CACHE_DIR)) ); // # clean up the .cache folders // rm -rf **/.cache/* await Promise.all( cacheDirs.map((cacheDir) => fs.promises.rm(cacheDir, { recursive: true })) ); } main();