diff --git a/__tests__/cache-base.test.ts b/__tests__/cache-base.test.ts new file mode 100644 index 0000000..0ae84fb --- /dev/null +++ b/__tests__/cache-base.test.ts @@ -0,0 +1,95 @@ +import {CacheEntryReport, CachingReport} from '../src/cache-base' + +describe('caching report', () => { + describe('reports not fully restored', () => { + it('with one requested entry report', async () => { + const report = new CachingReport() + report.entryReport('foo').markRequested('1', ['2']) + report.entryReport('bar').markRequested('3').markRestored('4') + expect(report.fullyRestored).toBe(false) + }) + }) + describe('reports fully restored', () => { + it('when empty', async () => { + const report = new CachingReport() + expect(report.fullyRestored).toBe(true) + }) + it('with empty entry reports', async () => { + const report = new CachingReport() + report.entryReport('foo') + report.entryReport('bar') + expect(report.fullyRestored).toBe(true) + }) + it('with restored entry report', async () => { + const report = new CachingReport() + report.entryReport('bar').markRequested('3').markRestored('4') + expect(report.fullyRestored).toBe(true) + }) + it('with multiple restored entry reportss', async () => { + const report = new CachingReport() + report.entryReport('foo').markRestored('4') + report.entryReport('bar').markRequested('3').markRestored('4') + expect(report.fullyRestored).toBe(true) + }) + }) + describe('can be stringified and rehydrated', () => { + it('when empty', async () => { + const report = new CachingReport() + + const stringRep = report.stringify() + const reportClone: CachingReport = CachingReport.rehydrate(stringRep) + + expect(reportClone.cacheEntryReports).toEqual([]) + + // Can call methods on rehydrated + expect(reportClone.entryReport('foo')).toBeInstanceOf(CacheEntryReport) + }) + it('with entry reports', async () => { + const report = new CachingReport() + report.entryReport('foo') + report.entryReport('bar') + report.entryReport('baz') + + const stringRep = report.stringify() + const reportClone: CachingReport = CachingReport.rehydrate(stringRep) + + expect(reportClone.cacheEntryReports.length).toBe(3) + expect(reportClone.cacheEntryReports[0].entryName).toBe('foo') + expect(reportClone.cacheEntryReports[1].entryName).toBe('bar') + expect(reportClone.cacheEntryReports[2].entryName).toBe('baz') + + expect(reportClone.entryReport('foo')).toBe(reportClone.cacheEntryReports[0]) + }) + it('with rehydrated entry report', async () => { + const report = new CachingReport() + const entryReport = report.entryReport('foo') + entryReport.markRequested('1', ['2', '3']) + entryReport.markSaved('4') + + const stringRep = report.stringify() + const reportClone: CachingReport = CachingReport.rehydrate(stringRep) + const entryClone = reportClone.entryReport('foo') + + expect(entryClone.requestedKey).toBe('1') + expect(entryClone.requestedRestoreKeys).toEqual(['2', '3']) + expect(entryClone.savedKey).toBe('4') + }) + it('with live entry report', async () => { + const report = new CachingReport() + const entryReport = report.entryReport('foo') + entryReport.markRequested('1', ['2', '3']) + + const stringRep = report.stringify() + const reportClone: CachingReport = CachingReport.rehydrate(stringRep) + const entryClone = reportClone.entryReport('foo') + + // Check type and call method on rehydrated entry report + expect(entryClone).toBeInstanceOf(CacheEntryReport) + entryClone.markSaved('4') + + expect(entryClone.requestedKey).toBe('1') + expect(entryClone.requestedRestoreKeys).toEqual(['2', '3']) + expect(entryClone.savedKey).toBe('4') + }) + }) +}) diff --git a/__tests__/cache-utils.test.ts b/__tests__/cache-utils.test.ts index d78d2ef..c79ec09 100644 --- a/__tests__/cache-utils.test.ts +++ b/__tests__/cache-utils.test.ts @@ -17,97 +17,4 @@ describe('cacheUtils-utils', () => { expect(posixHash).toBe(windowsHash) }) }) - describe('caching report', () => { - describe('reports not fully restored', () => { - it('with one requested entry report', async () => { - const report = new cacheUtils.CachingReport() - report.entryReport('foo').markRequested('1', ['2']) - report.entryReport('bar').markRequested('3').markRestored('4') - expect(report.fullyRestored).toBe(false) - }) - }) - describe('reports fully restored', () => { - it('when empty', async () => { - const report = new cacheUtils.CachingReport() - expect(report.fullyRestored).toBe(true) - }) - it('with empty entry reports', async () => { - const report = new cacheUtils.CachingReport() - report.entryReport('foo') - report.entryReport('bar') - expect(report.fullyRestored).toBe(true) - }) - it('with restored entry report', async () => { - const report = new cacheUtils.CachingReport() - report.entryReport('bar').markRequested('3').markRestored('4') - expect(report.fullyRestored).toBe(true) - }) - it('with multiple restored entry reportss', async () => { - const report = new cacheUtils.CachingReport() - report.entryReport('foo').markRestored('4') - report.entryReport('bar').markRequested('3').markRestored('4') - expect(report.fullyRestored).toBe(true) - }) - }) - describe('can be stringified and rehydrated', () => { - it('when empty', async () => { - const report = new cacheUtils.CachingReport() - - const stringRep = report.stringify() - const reportClone: cacheUtils.CachingReport = cacheUtils.CachingReport.rehydrate(stringRep) - - expect(reportClone.cacheEntryReports).toEqual([]) - - // Can call methods on rehydrated - expect(reportClone.entryReport('foo')).toBeInstanceOf(cacheUtils.CacheEntryReport) - }) - it('with entry reports', async () => { - const report = new cacheUtils.CachingReport() - report.entryReport('foo') - report.entryReport('bar') - report.entryReport('baz') - - const stringRep = report.stringify() - const reportClone: cacheUtils.CachingReport = cacheUtils.CachingReport.rehydrate(stringRep) - - expect(reportClone.cacheEntryReports.length).toBe(3) - expect(reportClone.cacheEntryReports[0].entryName).toBe('foo') - expect(reportClone.cacheEntryReports[1].entryName).toBe('bar') - expect(reportClone.cacheEntryReports[2].entryName).toBe('baz') - - expect(reportClone.entryReport('foo')).toBe(reportClone.cacheEntryReports[0]) - }) - it('with rehydrated entry report', async () => { - const report = new cacheUtils.CachingReport() - const entryReport = report.entryReport('foo') - entryReport.markRequested('1', ['2', '3']) - entryReport.markSaved('4') - - const stringRep = report.stringify() - const reportClone: cacheUtils.CachingReport = cacheUtils.CachingReport.rehydrate(stringRep) - const entryClone = reportClone.entryReport('foo') - - expect(entryClone.requestedKey).toBe('1') - expect(entryClone.requestedRestoreKeys).toEqual(['2', '3']) - expect(entryClone.savedKey).toBe('4') - }) - it('with live entry report', async () => { - const report = new cacheUtils.CachingReport() - const entryReport = report.entryReport('foo') - entryReport.markRequested('1', ['2', '3']) - - const stringRep = report.stringify() - const reportClone: cacheUtils.CachingReport = cacheUtils.CachingReport.rehydrate(stringRep) - const entryClone = reportClone.entryReport('foo') - - // Check type and call method on rehydrated entry report - expect(entryClone).toBeInstanceOf(cacheUtils.CacheEntryReport) - entryClone.markSaved('4') - - expect(entryClone.requestedKey).toBe('1') - expect(entryClone.requestedRestoreKeys).toEqual(['2', '3']) - expect(entryClone.savedKey).toBe('4') - }) - }) - }) }) diff --git a/src/cache-base.ts b/src/cache-base.ts new file mode 100644 index 0000000..dc4a53b --- /dev/null +++ b/src/cache-base.ts @@ -0,0 +1,253 @@ +import * as core from '@actions/core' +import * as cache from '@actions/cache' +import * as github from '@actions/github' +import {isCacheDebuggingEnabled, getCacheKeyPrefix, hashStrings} from './cache-utils' + +const JOB_CONTEXT_PARAMETER = 'workflow-job-context' + +function generateCacheKey(cacheName: string): CacheKey { + const cacheKeyPrefix = getCacheKeyPrefix() + + // At the most general level, share caches for all executions on the same OS + const runnerOs = process.env['RUNNER_OS'] || '' + const cacheKeyForOs = `${cacheKeyPrefix}${cacheName}|${runnerOs}` + + // Prefer caches that run this job + const cacheKeyForJob = `${cacheKeyForOs}|${github.context.job}` + + // Prefer (even more) jobs that run this job with the same context (matrix) + const cacheKeyForJobContext = `${cacheKeyForJob}[${determineJobContext()}]` + + // Exact match on Git SHA + const cacheKey = `${cacheKeyForJobContext}-${github.context.sha}` + + return new CacheKey(cacheKey, [cacheKeyForJobContext, cacheKeyForJob, cacheKeyForOs]) +} + +function determineJobContext(): string { + // By default, we hash the full `matrix` data for the run, to uniquely identify this job invocation + const workflowJobContext = core.getInput(JOB_CONTEXT_PARAMETER) + return hashStrings([workflowJobContext]) +} + +class CacheKey { + key: string + restoreKeys: string[] + + constructor(key: string, restoreKeys: string[]) { + this.key = key + this.restoreKeys = restoreKeys + } +} + +export class CachingReport { + cacheEntryReports: CacheEntryReport[] = [] + + get fullyRestored(): boolean { + return this.cacheEntryReports.every(x => !x.wasRequestedButNotRestored()) + } + + entryReport(name: string): CacheEntryReport { + for (const report of this.cacheEntryReports) { + if (report.entryName === name) { + return report + } + } + + const newReport = new CacheEntryReport(name) + this.cacheEntryReports.push(newReport) + return newReport + } + + stringify(): string { + return JSON.stringify(this) + } + + static rehydrate(stringRep: string): CachingReport { + const rehydrated: CachingReport = Object.assign(new CachingReport(), JSON.parse(stringRep)) + const entryReports = rehydrated.cacheEntryReports + for (let index = 0; index < entryReports.length; index++) { + const rawEntryReport = entryReports[index] + entryReports[index] = Object.assign(new CacheEntryReport(rawEntryReport.entryName), rawEntryReport) + } + return rehydrated + } +} + +export class CacheEntryReport { + entryName: string + requestedKey: string | undefined + requestedRestoreKeys: string[] | undefined + restoredKey: string | undefined + restoredSize: number | undefined + + savedKey: string | undefined + savedSize: number | undefined + + constructor(entryName: string) { + this.entryName = entryName + } + + wasRequestedButNotRestored(): boolean { + return this.requestedKey !== undefined && this.restoredKey === undefined + } + + markRequested(key: string, restoreKeys: string[] = []): CacheEntryReport { + this.requestedKey = key + this.requestedRestoreKeys = restoreKeys + return this + } + + markRestored(key: string): CacheEntryReport { + this.restoredKey = key + return this + } + + markSaved(key: string): CacheEntryReport { + this.savedKey = key + return this + } +} + +export abstract class AbstractCache { + private cacheName: string + private cacheDescription: string + private cacheKeyStateKey: string + private cacheResultStateKey: string + + protected readonly cacheDebuggingEnabled: boolean + + constructor(cacheName: string, cacheDescription: string) { + this.cacheName = cacheName + this.cacheDescription = cacheDescription + this.cacheKeyStateKey = `CACHE_KEY_${cacheName}` + this.cacheResultStateKey = `CACHE_RESULT_${cacheName}` + this.cacheDebuggingEnabled = isCacheDebuggingEnabled() + } + + async restore(report: CachingReport): Promise { + if (this.cacheOutputExists()) { + core.info(`${this.cacheDescription} already exists. Not restoring from cache.`) + return + } + + const cacheKey = this.prepareCacheKey() + const entryReport = report.entryReport(this.cacheName) + entryReport.markRequested(cacheKey.key, cacheKey.restoreKeys) + + this.debug( + `Requesting ${this.cacheDescription} with + key:${cacheKey.key} + restoreKeys:[${cacheKey.restoreKeys}]` + ) + + const cacheResult = await this.restoreCache(this.getCachePath(), cacheKey.key, cacheKey.restoreKeys) + + if (!cacheResult) { + core.info(`${this.cacheDescription} cache not found. Will start with empty.`) + return + } + + core.saveState(this.cacheResultStateKey, cacheResult) + entryReport.markRestored(cacheResult) + core.info(`Restored ${this.cacheDescription} from cache key: ${cacheResult}`) + + try { + await this.afterRestore(report) + } catch (error) { + core.warning(`Restore ${this.cacheDescription} failed in 'afterRestore': ${error}`) + } + } + + prepareCacheKey(): CacheKey { + const cacheKey = generateCacheKey(this.cacheName) + + core.saveState(this.cacheKeyStateKey, cacheKey.key) + return cacheKey + } + + protected async restoreCache( + cachePath: string[], + cacheKey: string, + cacheRestoreKeys: string[] = [] + ): Promise { + try { + return await cache.restoreCache(cachePath, cacheKey, cacheRestoreKeys) + } catch (error) { + if (error instanceof cache.ValidationError) { + // Validation errors should fail the build action + throw error + } + // Warn about any other error and continue + core.warning(`Failed to restore ${cacheKey}: ${error}`) + return undefined + } + } + + protected async afterRestore(_report: CachingReport): Promise {} + + async save(report: CachingReport): Promise { + if (!this.cacheOutputExists()) { + this.debug(`No ${this.cacheDescription} to cache.`) + return + } + + const cacheKey = core.getState(this.cacheKeyStateKey) + const cacheResult = core.getState(this.cacheResultStateKey) + + if (!cacheKey) { + this.debug(`${this.cacheDescription} existed prior to cache restore. Not saving.`) + return + } + + if (cacheResult && cacheKey === cacheResult) { + core.info(`Cache hit occurred on the cache key ${cacheKey}, not saving cache.`) + return + } + + try { + await this.beforeSave(report) + } catch (error) { + core.warning(`Save ${this.cacheDescription} failed in 'beforeSave': ${error}`) + return + } + + core.info(`Caching ${this.cacheDescription} with cache key: ${cacheKey}`) + const cachePath = this.getCachePath() + await this.saveCache(cachePath, cacheKey) + + report.entryReport(this.cacheName).markSaved(cacheKey) + + return + } + + protected async beforeSave(_report: CachingReport): Promise {} + + protected async saveCache(cachePath: string[], cacheKey: string): Promise { + try { + await cache.saveCache(cachePath, cacheKey) + } catch (error) { + if (error instanceof cache.ValidationError) { + // Validation errors should fail the build action + throw error + } else if (error instanceof cache.ReserveCacheError) { + // Reserve cache errors are expected if the artifact has been previously cached + this.debug(error.message) + } else { + // Warn about any other error and continue + core.warning(String(error)) + } + } + } + + protected debug(message: string): void { + if (this.cacheDebuggingEnabled) { + core.info(message) + } else { + core.debug(message) + } + } + + protected abstract cacheOutputExists(): boolean + protected abstract getCachePath(): string[] +} diff --git a/src/cache-gradle-user-home.ts b/src/cache-gradle-user-home.ts index f8134ac..115a32a 100644 --- a/src/cache-gradle-user-home.ts +++ b/src/cache-gradle-user-home.ts @@ -5,14 +5,8 @@ import * as core from '@actions/core' import * as glob from '@actions/glob' import * as exec from '@actions/exec' -import { - AbstractCache, - CacheEntryReport, - CachingReport, - getCacheKeyPrefix, - hashFileNames, - tryDelete -} from './cache-utils' +import {AbstractCache, CacheEntryReport, CachingReport} from './cache-base' +import {getCacheKeyPrefix, hashFileNames, tryDelete} from './cache-utils' const META_FILE_DIR = '.gradle-build-action' diff --git a/src/cache-project-dot-gradle.ts b/src/cache-project-dot-gradle.ts index 06ff98a..fa6c218 100644 --- a/src/cache-project-dot-gradle.ts +++ b/src/cache-project-dot-gradle.ts @@ -1,6 +1,6 @@ import path from 'path' import fs from 'fs' -import {AbstractCache} from './cache-utils' +import {AbstractCache} from './cache-base' // TODO: Maybe allow the user to override / tweak this set const PATHS_TO_CACHE = [ diff --git a/src/cache-utils.ts b/src/cache-utils.ts index 54f4f38..1360f7d 100644 --- a/src/cache-utils.ts +++ b/src/cache-utils.ts @@ -1,6 +1,4 @@ import * as core from '@actions/core' -import * as cache from '@actions/cache' -import * as github from '@actions/github' import * as crypto from 'crypto' import * as path from 'path' import * as fs from 'fs' @@ -9,7 +7,6 @@ const CACHE_PROTOCOL_VERSION = 'v4-' const CACHE_DISABLED_PARAMETER = 'cache-disabled' const CACHE_READONLY_PARAMETER = 'cache-read-only' -const JOB_CONTEXT_PARAMETER = 'workflow-job-context' const CACHE_DEBUG_VAR = 'GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED' const CACHE_PREFIX_VAR = 'GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX' @@ -30,31 +27,6 @@ export function getCacheKeyPrefix(): string { return process.env[CACHE_PREFIX_VAR] || CACHE_PROTOCOL_VERSION } -function generateCacheKey(cacheName: string): CacheKey { - const cacheKeyPrefix = getCacheKeyPrefix() - - // At the most general level, share caches for all executions on the same OS - const runnerOs = process.env['RUNNER_OS'] || '' - const cacheKeyForOs = `${cacheKeyPrefix}${cacheName}|${runnerOs}` - - // Prefer caches that run this job - const cacheKeyForJob = `${cacheKeyForOs}|${github.context.job}` - - // Prefer (even more) jobs that run this job with the same context (matrix) - const cacheKeyForJobContext = `${cacheKeyForJob}[${determineJobContext()}]` - - // Exact match on Git SHA - const cacheKey = `${cacheKeyForJobContext}-${github.context.sha}` - - return new CacheKey(cacheKey, [cacheKeyForJobContext, cacheKeyForJob, cacheKeyForOs]) -} - -function determineJobContext(): string { - // By default, we hash the full `matrix` data for the run, to uniquely identify this job invocation - const workflowJobContext = core.getInput(JOB_CONTEXT_PARAMETER) - return hashStrings([workflowJobContext]) -} - export function hashStrings(values: string[]): string { const hash = crypto.createHash('md5') for (const value of values) { @@ -94,225 +66,3 @@ export async function tryDelete(file: string): Promise { async function delay(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)) } - -class CacheKey { - key: string - restoreKeys: string[] - - constructor(key: string, restoreKeys: string[]) { - this.key = key - this.restoreKeys = restoreKeys - } -} - -export class CachingReport { - cacheEntryReports: CacheEntryReport[] = [] - - get fullyRestored(): boolean { - return this.cacheEntryReports.every(x => !x.wasRequestedButNotRestored()) - } - - entryReport(name: string): CacheEntryReport { - for (const report of this.cacheEntryReports) { - if (report.entryName === name) { - return report - } - } - - const newReport = new CacheEntryReport(name) - this.cacheEntryReports.push(newReport) - return newReport - } - - stringify(): string { - return JSON.stringify(this) - } - - static rehydrate(stringRep: string): CachingReport { - const rehydrated: CachingReport = Object.assign(new CachingReport(), JSON.parse(stringRep)) - const entryReports = rehydrated.cacheEntryReports - for (let index = 0; index < entryReports.length; index++) { - const rawEntryReport = entryReports[index] - entryReports[index] = Object.assign(new CacheEntryReport(rawEntryReport.entryName), rawEntryReport) - } - return rehydrated - } -} - -export class CacheEntryReport { - entryName: string - requestedKey: string | undefined - requestedRestoreKeys: string[] | undefined - restoredKey: string | undefined - restoredSize: number | undefined - - savedKey: string | undefined - savedSize: number | undefined - - constructor(entryName: string) { - this.entryName = entryName - } - - wasRequestedButNotRestored(): boolean { - return this.requestedKey !== undefined && this.restoredKey === undefined - } - - markRequested(key: string, restoreKeys: string[] = []): CacheEntryReport { - this.requestedKey = key - this.requestedRestoreKeys = restoreKeys - return this - } - - markRestored(key: string): CacheEntryReport { - this.restoredKey = key - return this - } - - markSaved(key: string): CacheEntryReport { - this.savedKey = key - return this - } -} - -export abstract class AbstractCache { - private cacheName: string - private cacheDescription: string - private cacheKeyStateKey: string - private cacheResultStateKey: string - - protected readonly cacheDebuggingEnabled: boolean - - constructor(cacheName: string, cacheDescription: string) { - this.cacheName = cacheName - this.cacheDescription = cacheDescription - this.cacheKeyStateKey = `CACHE_KEY_${cacheName}` - this.cacheResultStateKey = `CACHE_RESULT_${cacheName}` - this.cacheDebuggingEnabled = isCacheDebuggingEnabled() - } - - async restore(report: CachingReport): Promise { - if (this.cacheOutputExists()) { - core.info(`${this.cacheDescription} already exists. Not restoring from cache.`) - return - } - - const cacheKey = this.prepareCacheKey() - const entryReport = report.entryReport(this.cacheName) - entryReport.markRequested(cacheKey.key, cacheKey.restoreKeys) - - this.debug( - `Requesting ${this.cacheDescription} with - key:${cacheKey.key} - restoreKeys:[${cacheKey.restoreKeys}]` - ) - - const cacheResult = await this.restoreCache(this.getCachePath(), cacheKey.key, cacheKey.restoreKeys) - - if (!cacheResult) { - core.info(`${this.cacheDescription} cache not found. Will start with empty.`) - return - } - - core.saveState(this.cacheResultStateKey, cacheResult) - entryReport.markRestored(cacheResult) - core.info(`Restored ${this.cacheDescription} from cache key: ${cacheResult}`) - - try { - await this.afterRestore(report) - } catch (error) { - core.warning(`Restore ${this.cacheDescription} failed in 'afterRestore': ${error}`) - } - } - - prepareCacheKey(): CacheKey { - const cacheKey = generateCacheKey(this.cacheName) - - core.saveState(this.cacheKeyStateKey, cacheKey.key) - return cacheKey - } - - protected async restoreCache( - cachePath: string[], - cacheKey: string, - cacheRestoreKeys: string[] = [] - ): Promise { - try { - return await cache.restoreCache(cachePath, cacheKey, cacheRestoreKeys) - } catch (error) { - if (error instanceof cache.ValidationError) { - // Validation errors should fail the build action - throw error - } - // Warn about any other error and continue - core.warning(`Failed to restore ${cacheKey}: ${error}`) - return undefined - } - } - - protected async afterRestore(_report: CachingReport): Promise {} - - async save(report: CachingReport): Promise { - if (!this.cacheOutputExists()) { - this.debug(`No ${this.cacheDescription} to cache.`) - return - } - - const cacheKey = core.getState(this.cacheKeyStateKey) - const cacheResult = core.getState(this.cacheResultStateKey) - - if (!cacheKey) { - this.debug(`${this.cacheDescription} existed prior to cache restore. Not saving.`) - return - } - - if (cacheResult && cacheKey === cacheResult) { - core.info(`Cache hit occurred on the cache key ${cacheKey}, not saving cache.`) - return - } - - try { - await this.beforeSave(report) - } catch (error) { - core.warning(`Save ${this.cacheDescription} failed in 'beforeSave': ${error}`) - return - } - - core.info(`Caching ${this.cacheDescription} with cache key: ${cacheKey}`) - const cachePath = this.getCachePath() - await this.saveCache(cachePath, cacheKey) - - report.entryReport(this.cacheName).markSaved(cacheKey) - - return - } - - protected async beforeSave(_report: CachingReport): Promise {} - - protected async saveCache(cachePath: string[], cacheKey: string): Promise { - try { - await cache.saveCache(cachePath, cacheKey) - } catch (error) { - if (error instanceof cache.ValidationError) { - // Validation errors should fail the build action - throw error - } else if (error instanceof cache.ReserveCacheError) { - // Reserve cache errors are expected if the artifact has been previously cached - this.debug(error.message) - } else { - // Warn about any other error and continue - core.warning(String(error)) - } - } - } - - protected debug(message: string): void { - if (this.cacheDebuggingEnabled) { - core.info(message) - } else { - core.debug(message) - } - } - - protected abstract cacheOutputExists(): boolean - protected abstract getCachePath(): string[] -} diff --git a/src/caches.ts b/src/caches.ts index eac1ecc..555e673 100644 --- a/src/caches.ts +++ b/src/caches.ts @@ -1,7 +1,8 @@ import {GradleUserHomeCache} from './cache-gradle-user-home' import {ProjectDotGradleCache} from './cache-project-dot-gradle' import * as core from '@actions/core' -import {CachingReport, isCacheDisabled, isCacheReadOnly} from './cache-utils' +import {isCacheDisabled, isCacheReadOnly} from './cache-utils' +import {CachingReport} from './cache-base' const BUILD_ROOT_DIR = 'BUILD_ROOT_DIR' const CACHING_REPORT = 'CACHING_REPORT'