|
| 1 | +import process from "node:process"; |
| 2 | +import cache from "@actions/cache"; |
| 3 | +import core from "@actions/core"; |
| 4 | + |
| 5 | +const state = { |
| 6 | + DENO_DIR: "DENO_DIR", |
| 7 | + CACHE_HIT: "CACHE_HIT", |
| 8 | + CACHE_SAVE: "CACHE_SAVE", |
| 9 | +}; |
| 10 | + |
| 11 | +export async function saveCache() { |
| 12 | + if (!cache.isFeatureAvailable()) { |
| 13 | + core.warning("Caching is not available. Caching is skipped."); |
| 14 | + return; |
| 15 | + } |
| 16 | + |
| 17 | + const denoDir = core.getState(state.DENO_DIR); |
| 18 | + const saveKey = core.getState(state.CACHE_SAVE); |
| 19 | + if (!denoDir || !saveKey) { |
| 20 | + core.info("Caching is not enabled. Caching is skipped."); |
| 21 | + return; |
| 22 | + } else if (core.getState(state.CACHE_HIT) === "true") { |
| 23 | + core.info( |
| 24 | + `Cache hit occurred on the primary key "${saveKey}", not saving cache.`, |
| 25 | + ); |
| 26 | + return; |
| 27 | + } |
| 28 | + |
| 29 | + await cache.saveCache([denoDir], saveKey); |
| 30 | + core.info(`Cache saved with key: "${saveKey}".`); |
| 31 | +} |
| 32 | + |
| 33 | +/** |
| 34 | + * @param {string} cacheHash Should be a hash of any lockfiles or similar. |
| 35 | + */ |
| 36 | +export async function restoreCache(cacheHash) { |
| 37 | + try { |
| 38 | + const denoDir = await resolveDenoDir(); |
| 39 | + core.saveState(state.DENO_DIR, denoDir); |
| 40 | + |
| 41 | + const { GITHUB_JOB, RUNNER_OS, RUNNER_ARCH } = process.env; |
| 42 | + const restoreKey = `deno-cache-${RUNNER_OS}-${RUNNER_ARCH}`; |
| 43 | + // CI jobs often download different dependencies, so include Job ID in the cache key. |
| 44 | + const primaryKey = `${restoreKey}-${GITHUB_JOB}-${cacheHash}`; |
| 45 | + core.saveState(state.CACHE_SAVE, primaryKey); |
| 46 | + |
| 47 | + const loadedCacheKey = await cache.restoreCache([denoDir], primaryKey, [ |
| 48 | + restoreKey, |
| 49 | + ]); |
| 50 | + const cacheHit = primaryKey === loadedCacheKey; |
| 51 | + core.setOutput("cache-hit", cacheHit); |
| 52 | + core.saveState(state.CACHE_HIT, cacheHit); |
| 53 | + |
| 54 | + const message = loadedCacheKey |
| 55 | + ? `Cache key used: "${loadedCacheKey}".` |
| 56 | + : `No cache found for restore key: "${restoreKey}".`; |
| 57 | + core.info(message); |
| 58 | + } catch (err) { |
| 59 | + core.warning( |
| 60 | + new Error("Failed to restore cache. Continuing without cache.", { |
| 61 | + cause: err, |
| 62 | + }), |
| 63 | + ); |
| 64 | + } |
| 65 | +} |
| 66 | + |
| 67 | +/** |
| 68 | + * @returns {Promise<string>} |
| 69 | + */ |
| 70 | +async function resolveDenoDir() { |
| 71 | + const { DENO_DIR } = process.env; |
| 72 | + if (DENO_DIR) return DENO_DIR; |
| 73 | + |
| 74 | + // Retrieve the DENO_DIR from `deno info --json` |
| 75 | + const { exec } = await import("node:child_process"); |
| 76 | + const output = await new Promise((res, rej) => { |
| 77 | + exec("deno info --json", (err, stdout) => err ? rej(err) : res(stdout)); |
| 78 | + }); |
| 79 | + return JSON.parse(output).denoDir; |
| 80 | +} |
0 commit comments