|
| 1 | +// Modified version of https://github.com/spring-io/antora-extensions/blob/fee110d465480db87f029426bfab7da8068e282e/lib/inject-collector-cache-config-extension.js |
| 2 | +// SPDX-License-Identifier: Apache-2.0 |
| 3 | + |
| 4 | +'use strict' |
| 5 | + |
| 6 | +const expandPath = require('@antora/expand-path-helper') |
| 7 | +const ospath = require('path') |
| 8 | +const resolvedCopyRecursiveJs = require.resolve('./cache-scandir') |
| 9 | +const { createHash } = require('crypto') |
| 10 | +// @actions/cache is ESM-only, so load it via dynamic import from this CommonJS module. |
| 11 | +const cacheLib = import('@actions/cache') |
| 12 | + |
| 13 | +module.exports.register = function ({ playbook, config = {} }) { |
| 14 | + const logger = this.getLogger('collector-cache') |
| 15 | + const excludes = config.excludes || [] |
| 16 | + const getUserCacheDir = this.require('cache-directory') |
| 17 | + const fs = this.require('fs') |
| 18 | + |
| 19 | + const cacheInfo = [] |
| 20 | + this.once('contentAggregated', async ({ playbook, contentAggregate }) => { |
| 21 | + const cache = await cacheLib |
| 22 | + const cacheAvailable = cache.isFeatureAvailable() |
| 23 | + if (!cacheAvailable) { |
| 24 | + logger.info('GitHub Actions cache is not available; skipping restore/save operations') |
| 25 | + } |
| 26 | + const baseCacheDir = getBaseCacheDir(getUserCacheDir, playbook) |
| 27 | + for (const { origins } of contentAggregate) { |
| 28 | + for (const origin of origins) { |
| 29 | + const { url, gitdir, refhash, worktree, descriptor, refname } = origin |
| 30 | + const repositoryCacheDirName = generateWorktreeFolderName({ url, gitdir, worktree }) |
| 31 | + const baseCollectorCacheCacheDir = ospath.join(baseCacheDir, 'collector-cache', repositoryCacheDirName) |
| 32 | + const collectorConfig = descriptor?.ext?.collector |
| 33 | + if (collectorConfig !== undefined && !excludes.includes(refname)) { |
| 34 | + const shortref = refhash.slice(0, 7) |
| 35 | + const cacheDirName = `${shortref}-${refname}` |
| 36 | + const cacheDir = ospath.join(baseCollectorCacheCacheDir, cacheDirName) |
| 37 | + const cacheKey = `collector-cache-${repositoryCacheDirName}-${cacheDirName}` |
| 38 | + if (!fs.existsSync(baseCollectorCacheCacheDir)) { |
| 39 | + fs.mkdirSync(baseCollectorCacheCacheDir, { recursive: true }) |
| 40 | + } |
| 41 | + if (cacheAvailable && !fs.existsSync(cacheDir)) { |
| 42 | + // try and restore from the GitHub Actions cache |
| 43 | + const restoredKey = await cache.restoreCache([cacheDir], cacheKey).catch((err) => { |
| 44 | + logger.warn(`Failed to restore cache for key ${cacheKey}: ${err.message}`) |
| 45 | + return undefined |
| 46 | + }) |
| 47 | + if (restoredKey) { |
| 48 | + logger.info(`Successfully restored cache for key ${cacheKey}`) |
| 49 | + } else { |
| 50 | + logger.info(`Unable to restore cache for key ${cacheKey}`) |
| 51 | + } |
| 52 | + } |
| 53 | + if (fs.existsSync(cacheDir)) { |
| 54 | + // use the cache |
| 55 | + origin.descriptor.ext.collector = { |
| 56 | + scan: { |
| 57 | + dir: cacheDir, |
| 58 | + }, |
| 59 | + } |
| 60 | + logger.info(`Using the cache found at ${cacheDir}`) |
| 61 | + } else { |
| 62 | + const cachedConfig = [] |
| 63 | + const normalizedCollectorConfig = Array.isArray(collectorConfig) ? collectorConfig : [collectorConfig] |
| 64 | + logger.info(`Configuring collector for caching (url: ${url} | ref: ${refname})`) |
| 65 | + origin.descriptor.ext.collector = cachedConfig |
| 66 | + normalizedCollectorConfig.forEach((collector) => { |
| 67 | + cachedConfig.push(collector) |
| 68 | + const { scan: scanConfig = [] } = collector |
| 69 | + const normalizedScanConfigs = Array.isArray(scanConfig) ? scanConfig : [scanConfig] |
| 70 | + normalizedScanConfigs.forEach((normalizedScanConfig) => { |
| 71 | + // cache the output of the build |
| 72 | + const cachedCollectorConfig = createCachedCollectorConfig(normalizedScanConfig.dir, cacheDir, normalizedScanConfig.into) |
| 73 | + cachedConfig.push.apply(cachedConfig, cachedCollectorConfig) |
| 74 | + }) |
| 75 | + }) |
| 76 | + if (cacheAvailable) { |
| 77 | + cacheInfo.push({ cacheDir, cacheKey }) |
| 78 | + } |
| 79 | + } |
| 80 | + } |
| 81 | + } |
| 82 | + } |
| 83 | + }) |
| 84 | + this.once('beforePublish', async () => { |
| 85 | + if (cacheInfo) { |
| 86 | + const cache = await cacheLib |
| 87 | + for (const {cacheDir, cacheKey} of cacheInfo) { |
| 88 | + await cache.saveCache([cacheDir], cacheKey) |
| 89 | + logger.info(`Saved cache for key ${cacheKey}`) |
| 90 | + } |
| 91 | + } |
| 92 | + }) |
| 93 | +} |
| 94 | + |
| 95 | +function getBaseCacheDir (getUserCacheDir, { dir: dot, runtime: { cacheDir: preferredDir } = {} }) { |
| 96 | + return preferredDir == null |
| 97 | + ? getUserCacheDir(`antora${process.env.NODE_ENV === 'test' ? '-test' : ''}`) || ospath.join(dot, '.cache/antora') |
| 98 | + : expandPath(preferredDir, { dot }) |
| 99 | +} |
| 100 | + |
| 101 | +function generateWorktreeFolderName ({ url, gitdir, worktree }) { |
| 102 | + if (worktree === undefined) return ospath.basename(gitdir, '.git') |
| 103 | + return `${url.substr(url.lastIndexOf('/') + 1)}-${createHash('sha1').update(url).digest('hex')}` |
| 104 | +} |
| 105 | + |
| 106 | +function createCachedCollectorConfig (scanDir, cacheDir, subDir) { |
| 107 | + return [ |
| 108 | + { |
| 109 | + run: { |
| 110 | + command: `\${{env.NODE}} '${resolvedCopyRecursiveJs}' '.' '${cacheDir}${subDir ? `/${subDir}` : ''}'`, |
| 111 | + dir: scanDir, |
| 112 | + }, |
| 113 | + }, |
| 114 | + ] |
| 115 | +} |
0 commit comments