mirror of
https://github.com/gradle/gradle-build-action.git
synced 2024-11-29 11:40:59 -05:00
Ensure save/restore only on first action step
Instead of relying on the separate cache implementations to check for the existence of cached products, we now explicitly track whether or not the execution is the first time the action has been invoked for a job.
This commit is contained in:
parent
253d6427fd
commit
1041604f29
4 changed files with 45 additions and 38 deletions
|
@ -83,21 +83,16 @@ export abstract class AbstractCache {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restores the cache entry, finding the closest match to the currently running job.
|
* Restores the cache entry, finding the closest match to the currently running job.
|
||||||
* If the target output already exists, caching will be skipped.
|
|
||||||
*/
|
*/
|
||||||
async restore(listener: CacheListener): Promise<void> {
|
async restore(listener: CacheListener): Promise<void> {
|
||||||
if (this.cacheOutputExists()) {
|
|
||||||
core.info(`${this.cacheDescription} already exists. Not restoring from cache.`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const entryListener = listener.entry(this.cacheDescription)
|
const entryListener = listener.entry(this.cacheDescription)
|
||||||
|
|
||||||
const cacheKey = this.prepareCacheKey()
|
const cacheKey = this.prepareCacheKey()
|
||||||
|
|
||||||
this.debug(
|
this.debug(
|
||||||
`Requesting ${this.cacheDescription} with
|
`Requesting ${this.cacheDescription} with
|
||||||
key:${cacheKey.key}
|
key:${cacheKey.key}
|
||||||
restoreKeys:[${cacheKey.restoreKeys}]`
|
restoreKeys:[${cacheKey.restoreKeys}]`
|
||||||
)
|
)
|
||||||
|
|
||||||
const cacheResult = await this.restoreCache(this.getCachePath(), cacheKey.key, cacheKey.restoreKeys)
|
const cacheResult = await this.restoreCache(this.getCachePath(), cacheKey.key, cacheKey.restoreKeys)
|
||||||
|
@ -142,28 +137,17 @@ export abstract class AbstractCache {
|
||||||
protected async afterRestore(_listener: CacheListener): Promise<void> {}
|
protected async afterRestore(_listener: CacheListener): Promise<void> {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the cache entry based on the current cache key, unless:
|
* Saves the cache entry based on the current cache key unless the cache was restored with the exact key,
|
||||||
* - If the cache output existed before restore, then it is not saved.
|
* in which case we cannot overwrite it.
|
||||||
* - If the cache was restored with the exact key, we cannot overwrite it.
|
|
||||||
*
|
*
|
||||||
* If the cache entry was restored with a partial match on a restore key, then
|
* If the cache entry was restored with a partial match on a restore key, then
|
||||||
* it is saved with the exact key.
|
* it is saved with the exact key.
|
||||||
*/
|
*/
|
||||||
async save(listener: CacheListener): Promise<void> {
|
async save(listener: CacheListener): Promise<void> {
|
||||||
if (!this.cacheOutputExists()) {
|
|
||||||
core.info(`No ${this.cacheDescription} to cache.`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve the state set in the previous 'restore' step.
|
// Retrieve the state set in the previous 'restore' step.
|
||||||
const cacheKeyFromRestore = core.getState(this.cacheKeyStateKey)
|
const cacheKeyFromRestore = core.getState(this.cacheKeyStateKey)
|
||||||
const cacheResultFromRestore = core.getState(this.cacheResultStateKey)
|
const cacheResultFromRestore = core.getState(this.cacheResultStateKey)
|
||||||
|
|
||||||
if (!cacheKeyFromRestore) {
|
|
||||||
core.info(`${this.cacheDescription} existed prior to cache restore. Not saving.`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cacheResultFromRestore && cacheKeyFromRestore === cacheResultFromRestore) {
|
if (cacheResultFromRestore && cacheKeyFromRestore === cacheResultFromRestore) {
|
||||||
core.info(`Cache hit occurred on the cache key ${cacheKeyFromRestore}, not saving cache.`)
|
core.info(`Cache hit occurred on the cache key ${cacheKeyFromRestore}, not saving cache.`)
|
||||||
return
|
return
|
||||||
|
@ -206,6 +190,5 @@ export abstract class AbstractCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract cacheOutputExists(): boolean
|
|
||||||
protected abstract getCachePath(): string[]
|
protected abstract getCachePath(): string[]
|
||||||
}
|
}
|
||||||
|
|
|
@ -294,12 +294,6 @@ export class GradleUserHomeCache extends AbstractCache {
|
||||||
return path.resolve(os.homedir(), '.gradle')
|
return path.resolve(os.homedir(), '.gradle')
|
||||||
}
|
}
|
||||||
|
|
||||||
protected cacheOutputExists(): boolean {
|
|
||||||
// Need to check for 'caches' directory to avoid incorrect detection on MacOS agents
|
|
||||||
const dir = path.resolve(this.gradleUserHome, 'caches')
|
|
||||||
return fs.existsSync(dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines the paths within Gradle User Home to cache.
|
* Determines the paths within Gradle User Home to cache.
|
||||||
* By default, this is the 'caches' and 'notifications' directories,
|
* By default, this is the 'caches' and 'notifications' directories,
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import fs from 'fs'
|
|
||||||
import {AbstractCache} from './cache-base'
|
import {AbstractCache} from './cache-base'
|
||||||
|
|
||||||
// TODO: Maybe allow the user to override / tweak this set
|
// TODO: Maybe allow the user to override / tweak this set
|
||||||
|
@ -17,11 +16,6 @@ export class ProjectDotGradleCache extends AbstractCache {
|
||||||
this.rootDir = rootDir
|
this.rootDir = rootDir
|
||||||
}
|
}
|
||||||
|
|
||||||
protected cacheOutputExists(): boolean {
|
|
||||||
const dir = this.getProjectDotGradleDir()
|
|
||||||
return fs.existsSync(dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getCachePath(): string[] {
|
protected getCachePath(): string[] {
|
||||||
const dir = this.getProjectDotGradleDir()
|
const dir = this.getProjectDotGradleDir()
|
||||||
return PATHS_TO_CACHE.map(x => path.resolve(dir, x))
|
return PATHS_TO_CACHE.map(x => path.resolve(dir, x))
|
||||||
|
|
|
@ -4,20 +4,20 @@ import {ProjectDotGradleCache} from './cache-project-dot-gradle'
|
||||||
import {isCacheDisabled, isCacheReadOnly} from './cache-utils'
|
import {isCacheDisabled, isCacheReadOnly} from './cache-utils'
|
||||||
import {logCachingReport, CacheListener} from './cache-reporting'
|
import {logCachingReport, CacheListener} from './cache-reporting'
|
||||||
|
|
||||||
|
const CACHE_RESTORED_VAR = 'GRADLE_BUILD_ACTION_CACHE_RESTORED'
|
||||||
const BUILD_ROOT_DIR = 'BUILD_ROOT_DIR'
|
const BUILD_ROOT_DIR = 'BUILD_ROOT_DIR'
|
||||||
const CACHE_LISTENER = 'CACHE_LISTENER'
|
const CACHE_LISTENER = 'CACHE_LISTENER'
|
||||||
|
|
||||||
export async function restore(buildRootDirectory: string): Promise<void> {
|
export async function restore(buildRootDirectory: string): Promise<void> {
|
||||||
|
if (!shouldRestoreCaches()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const gradleUserHomeCache = new GradleUserHomeCache(buildRootDirectory)
|
const gradleUserHomeCache = new GradleUserHomeCache(buildRootDirectory)
|
||||||
const projectDotGradleCache = new ProjectDotGradleCache(buildRootDirectory)
|
const projectDotGradleCache = new ProjectDotGradleCache(buildRootDirectory)
|
||||||
|
|
||||||
gradleUserHomeCache.init()
|
gradleUserHomeCache.init()
|
||||||
|
|
||||||
if (isCacheDisabled()) {
|
|
||||||
core.info('Cache is disabled: will not restore state from previous builds.')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await core.group('Restore Gradle state from cache', async () => {
|
await core.group('Restore Gradle state from cache', async () => {
|
||||||
core.saveState(BUILD_ROOT_DIR, buildRootDirectory)
|
core.saveState(BUILD_ROOT_DIR, buildRootDirectory)
|
||||||
|
|
||||||
|
@ -38,6 +38,10 @@ export async function restore(buildRootDirectory: string): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function save(): Promise<void> {
|
export async function save(): Promise<void> {
|
||||||
|
if (!shouldSaveCaches()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const cacheListener: CacheListener = CacheListener.rehydrate(core.getState(CACHE_LISTENER))
|
const cacheListener: CacheListener = CacheListener.rehydrate(core.getState(CACHE_LISTENER))
|
||||||
|
|
||||||
if (isCacheReadOnly()) {
|
if (isCacheReadOnly()) {
|
||||||
|
@ -56,3 +60,35 @@ export async function save(): Promise<void> {
|
||||||
|
|
||||||
logCachingReport(cacheListener)
|
logCachingReport(cacheListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function shouldRestoreCaches(): boolean {
|
||||||
|
if (isCacheDisabled()) {
|
||||||
|
core.info('Cache is disabled: will not restore state from previous builds.')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env[CACHE_RESTORED_VAR]) {
|
||||||
|
core.info('Cache only restored on first action step.')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export var that is detected in all later restore steps
|
||||||
|
core.exportVariable(CACHE_RESTORED_VAR, true)
|
||||||
|
// Export state that is detected in corresponding post-action step
|
||||||
|
core.saveState(CACHE_RESTORED_VAR, true)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldSaveCaches(): boolean {
|
||||||
|
if (isCacheDisabled()) {
|
||||||
|
core.info('Cache is disabled: will not save state for later builds.')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!core.getState(CACHE_RESTORED_VAR)) {
|
||||||
|
core.info('Cache will only be saved in final post-action step.')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue