/** * Copyright 2023 actions-toolkit authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import fs from 'fs'; import os from 'os'; import path from 'path'; import * as core from '@actions/core'; import * as tc from '@actions/tool-cache'; import * as cache from '@actions/cache'; import * as util from 'util'; export interface CacheOpts { htcName: string; htcVersion: string; baseCacheDir: string; cacheFile: string; ghaNoCache?: boolean; } export interface CachePostState { dir: string; key: string; } export class Cache { private readonly opts: CacheOpts; private readonly ghaCacheKey: string; private readonly ghaNoCache?: boolean; private readonly cacheDir: string; private readonly cachePath: string; private static readonly POST_CACHE_KEY = 'postCache'; constructor(opts: CacheOpts) { this.opts = opts; this.ghaCacheKey = util.format('%s-%s-%s', this.opts.htcName, this.opts.htcVersion, this.platform()); this.ghaNoCache = this.opts.ghaNoCache; this.cacheDir = path.join(this.opts.baseCacheDir, this.opts.htcVersion, this.platform()); this.cachePath = path.join(this.cacheDir, this.opts.cacheFile); if (!fs.existsSync(this.cacheDir)) { fs.mkdirSync(this.cacheDir, {recursive: true}); } } public async save(file: string): Promise { core.debug(`Cache.save ${file}`); const cachePath = this.copyToCache(file); const htcPath = await tc.cacheDir(this.cacheDir, this.opts.htcName, this.opts.htcVersion, this.platform()); core.debug(`Cache.save cached to hosted tool cache ${htcPath}`); if (!this.ghaNoCache && cache.isFeatureAvailable()) { core.debug(`Cache.save sending ${this.ghaCacheKey} to post state`); core.saveState( Cache.POST_CACHE_KEY, JSON.stringify({ dir: this.cacheDir, key: this.ghaCacheKey } as CachePostState) ); } return cachePath; } public async find(): Promise { let htcPath = tc.find(this.opts.htcName, this.opts.htcVersion, this.platform()); if (htcPath) { core.info(`Restored from hosted tool cache ${htcPath}`); return this.copyToCache(`${htcPath}/${this.opts.cacheFile}`); } if (!this.ghaNoCache && cache.isFeatureAvailable()) { core.debug(`GitHub Actions cache feature available`); if (await cache.restoreCache([this.cacheDir], this.ghaCacheKey)) { core.info(`Restored ${this.ghaCacheKey} from GitHub Actions cache`); htcPath = await tc.cacheDir(this.cacheDir, this.opts.htcName, this.opts.htcVersion, this.platform()); core.info(`Cached to hosted tool cache ${htcPath}`); return this.copyToCache(`${htcPath}/${this.opts.cacheFile}`); } } else if (this.ghaNoCache) { core.info(`GitHub Actions cache disabled`); } else { core.info(`GitHub Actions cache feature not available`); } return ''; } public static async post(): Promise { const state = core.getState(Cache.POST_CACHE_KEY); if (!state) { core.info(`State not set`); return Promise.resolve(undefined); } let cacheState: CachePostState; try { cacheState = JSON.parse(state); } catch (e) { throw new Error(`Failed to parse cache post state: ${e}`); } if (!cacheState.dir || !cacheState.key) { throw new Error(`Invalid cache post state: ${state}`); } core.info(`Caching ${cacheState.key} to GitHub Actions cache`); await cache.saveCache([cacheState.dir], cacheState.key); return cacheState; } private copyToCache(file: string): string { core.debug(`Copying ${file} to ${this.cachePath}`); fs.copyFileSync(file, this.cachePath); return this.cachePath; } private platform(): string { // eslint-disable-next-line @typescript-eslint/no-explicit-any const arm_version = (process.config.variables as any).arm_version; return `${os.platform()}-${os.arch()}${arm_version ? 'v' + arm_version : ''}`; } }