mirror of
				https://github.com/gradle/gradle-build-action.git
				synced 2025-11-04 18:08:57 +08:00 
			
		
		
		
	Tidy-up caching code
- Extracted common code for Gradle User Home and Project .gradle caches into abstract supertype. - Improve error handling by checking error types
This commit is contained in:
		@@ -2,86 +2,26 @@ import path from 'path'
 | 
			
		||||
import fs from 'fs'
 | 
			
		||||
import os from 'os'
 | 
			
		||||
 | 
			
		||||
import * as core from '@actions/core'
 | 
			
		||||
import * as cache from '@actions/cache'
 | 
			
		||||
import {generateCacheKey} from './cache-utils'
 | 
			
		||||
import {AbstractCache} from './cache-utils'
 | 
			
		||||
 | 
			
		||||
const CACHE_PATH = [
 | 
			
		||||
    '~/.gradle/caches/*', // All directories in 'caches'
 | 
			
		||||
    '~/.gradle/notifications/*', // Prevent the re-rendering of first-use message for version
 | 
			
		||||
    '~/.gradle/wrapper/dists/*/*/*.zip' // Only wrapper zips are required : Gradle will expand these on demand
 | 
			
		||||
]
 | 
			
		||||
const CACHE_KEY = 'GUH_CACHE_KEY'
 | 
			
		||||
const CACHE_RESULT = 'GUH_CACHE_RESULT'
 | 
			
		||||
 | 
			
		||||
export async function restore(): Promise<void> {
 | 
			
		||||
    if (gradleUserHomeExists()) {
 | 
			
		||||
        core.info('Gradle User Home already exists. Not restoring from cache.')
 | 
			
		||||
        return
 | 
			
		||||
export class GradleUserHomeCache extends AbstractCache {
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super('gradle', 'Gradle User Home')
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const cacheKey = generateCacheKey('gradle')
 | 
			
		||||
 | 
			
		||||
    core.saveState(CACHE_KEY, cacheKey.key)
 | 
			
		||||
 | 
			
		||||
    const cacheResult = await cache.restoreCache(
 | 
			
		||||
        CACHE_PATH,
 | 
			
		||||
        cacheKey.key,
 | 
			
		||||
        cacheKey.restoreKeys
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    if (!cacheResult) {
 | 
			
		||||
        core.info(
 | 
			
		||||
            'Gradle User Home cache not found. Will start with empty home.'
 | 
			
		||||
        )
 | 
			
		||||
        return
 | 
			
		||||
    protected cacheOutputExists(): boolean {
 | 
			
		||||
        // Need to check for 'caches' directory to avoid incorrect detection on MacOS agents
 | 
			
		||||
        const dir = path.resolve(os.homedir(), '.gradle/caches')
 | 
			
		||||
        return fs.existsSync(dir)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    core.info(`Gradle User Home restored from cache key: ${cacheResult}`)
 | 
			
		||||
    return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function save(): Promise<void> {
 | 
			
		||||
    if (!gradleUserHomeExists()) {
 | 
			
		||||
        core.debug('No Gradle User Home to cache.')
 | 
			
		||||
        return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const cacheKey = core.getState(CACHE_KEY)
 | 
			
		||||
    const cacheResult = core.getState(CACHE_RESULT)
 | 
			
		||||
 | 
			
		||||
    if (!cacheKey) {
 | 
			
		||||
        core.info(
 | 
			
		||||
            'Gradle User Home 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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    core.info(`Caching Gradle User Home with cache key: ${cacheKey}`)
 | 
			
		||||
    try {
 | 
			
		||||
        await cache.saveCache(CACHE_PATH, cacheKey)
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
        if (error.name === cache.ValidationError.name) {
 | 
			
		||||
            throw error
 | 
			
		||||
        } else if (error.name === cache.ReserveCacheError.name) {
 | 
			
		||||
            core.info(error.message)
 | 
			
		||||
        } else {
 | 
			
		||||
            core.info(`[warning] ${error.message}`)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function gradleUserHomeExists(): boolean {
 | 
			
		||||
    // Need to check for 'caches' directory to avoid incorrect detection on MacOS agents
 | 
			
		||||
    const dir = path.resolve(os.homedir(), '.gradle/caches')
 | 
			
		||||
    return fs.existsSync(dir)
 | 
			
		||||
    protected getCachePath(): string[] {
 | 
			
		||||
        return CACHE_PATH
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,94 +1,29 @@
 | 
			
		||||
import path from 'path'
 | 
			
		||||
import fs from 'fs'
 | 
			
		||||
 | 
			
		||||
import * as core from '@actions/core'
 | 
			
		||||
import * as cache from '@actions/cache'
 | 
			
		||||
import {generateCacheKey} from './cache-utils'
 | 
			
		||||
import {AbstractCache} from './cache-utils'
 | 
			
		||||
 | 
			
		||||
const PATHS_TO_CACHE = [
 | 
			
		||||
    'configuration-cache' // Only configuration-cache is stored at present
 | 
			
		||||
]
 | 
			
		||||
const CACHE_KEY = 'PROJECT_CACHE_KEY'
 | 
			
		||||
const CACHE_RESULT = 'PROJECT_CACHE_RESULT'
 | 
			
		||||
 | 
			
		||||
export async function restore(rootDir: string): Promise<void> {
 | 
			
		||||
    if (projectDotGradleDirExists(rootDir)) {
 | 
			
		||||
        core.info(
 | 
			
		||||
            'Project .gradle directory already exists. Not restoring from cache.'
 | 
			
		||||
        )
 | 
			
		||||
        return
 | 
			
		||||
export class ProjectDotGradleCache extends AbstractCache {
 | 
			
		||||
    private rootDir: string
 | 
			
		||||
    constructor(rootDir: string) {
 | 
			
		||||
        super('project', 'Project .gradle directory')
 | 
			
		||||
        this.rootDir = rootDir
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const cacheKey = generateCacheKey('project')
 | 
			
		||||
 | 
			
		||||
    core.saveState(CACHE_KEY, cacheKey.key)
 | 
			
		||||
 | 
			
		||||
    const cacheResult = await cache.restoreCache(
 | 
			
		||||
        getCachePath(rootDir),
 | 
			
		||||
        cacheKey.key,
 | 
			
		||||
        cacheKey.restoreKeys
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    if (!cacheResult) {
 | 
			
		||||
        core.info('Project .gradle cache not found. Will start with empty.')
 | 
			
		||||
        return
 | 
			
		||||
    protected cacheOutputExists(): boolean {
 | 
			
		||||
        const dir = this.getProjectDotGradleDir()
 | 
			
		||||
        return fs.existsSync(dir)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    core.info(`Project .gradle dir restored from cache key: ${cacheResult}`)
 | 
			
		||||
    return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function save(rootDir: string): Promise<void> {
 | 
			
		||||
    if (!projectDotGradleDirExists(rootDir)) {
 | 
			
		||||
        core.debug('No project .gradle dir to cache.')
 | 
			
		||||
        return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const cacheKey = core.getState(CACHE_KEY)
 | 
			
		||||
    const cacheResult = core.getState(CACHE_RESULT)
 | 
			
		||||
 | 
			
		||||
    if (!cacheKey) {
 | 
			
		||||
        core.info(
 | 
			
		||||
            'Project .gradle dir 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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    core.info(`Caching project .gradle dir with cache key: ${cacheKey}`)
 | 
			
		||||
    try {
 | 
			
		||||
        await cache.saveCache(getCachePath(rootDir), cacheKey)
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
        if (error.name === cache.ValidationError.name) {
 | 
			
		||||
            throw error
 | 
			
		||||
        } else if (error.name === cache.ReserveCacheError.name) {
 | 
			
		||||
            core.info(error.message)
 | 
			
		||||
        } else {
 | 
			
		||||
            core.info(`[warning] ${error.message}`)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getCachePath(rootDir: string): string[] {
 | 
			
		||||
    const dir = getProjectDotGradleDir(rootDir)
 | 
			
		||||
    return PATHS_TO_CACHE.map(x => path.resolve(dir, x))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getProjectDotGradleDir(rootDir: string): string {
 | 
			
		||||
    core.debug(`Resolving .gradle dir in ${rootDir}`)
 | 
			
		||||
    return path.resolve(rootDir, '.gradle')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function projectDotGradleDirExists(rootDir: string): boolean {
 | 
			
		||||
    const dir = getProjectDotGradleDir(rootDir)
 | 
			
		||||
    core.debug(`Checking for existence of project .gradle dir: ${dir}`)
 | 
			
		||||
    return fs.existsSync(dir)
 | 
			
		||||
    protected getCachePath(): string[] {
 | 
			
		||||
        const dir = this.getProjectDotGradleDir()
 | 
			
		||||
        return PATHS_TO_CACHE.map(x => path.resolve(dir, x))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private getProjectDotGradleDir(): string {
 | 
			
		||||
        return path.resolve(this.rootDir, '.gradle')
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
import * as core from '@actions/core'
 | 
			
		||||
import * as cache from '@actions/cache'
 | 
			
		||||
import * as github from '@actions/github'
 | 
			
		||||
import * as crypto from 'crypto'
 | 
			
		||||
 | 
			
		||||
@@ -25,7 +26,7 @@ function getCacheEnabledValue(cacheName: string): string {
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function generateCacheKey(cacheName: string): CacheKey {
 | 
			
		||||
function generateCacheKey(cacheName: string): CacheKey {
 | 
			
		||||
    // Prefix can be used to force change all cache keys
 | 
			
		||||
    const cacheKeyPrefix = process.env['CACHE_KEY_PREFIX'] || ''
 | 
			
		||||
 | 
			
		||||
@@ -70,7 +71,7 @@ export function hashStrings(values: string[]): string {
 | 
			
		||||
    return hash.digest('hex')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class CacheKey {
 | 
			
		||||
class CacheKey {
 | 
			
		||||
    key: string
 | 
			
		||||
    restoreKeys: string[]
 | 
			
		||||
 | 
			
		||||
@@ -79,3 +80,95 @@ export class CacheKey {
 | 
			
		||||
        this.restoreKeys = restoreKeys
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export abstract class AbstractCache {
 | 
			
		||||
    private cacheName: string
 | 
			
		||||
    private cacheDescription: string
 | 
			
		||||
    private cacheKeyStateKey: string
 | 
			
		||||
    private cacheResultStateKey: string
 | 
			
		||||
 | 
			
		||||
    constructor(cacheName: string, cacheDescription: string) {
 | 
			
		||||
        this.cacheName = cacheName
 | 
			
		||||
        this.cacheDescription = cacheDescription
 | 
			
		||||
        this.cacheKeyStateKey = `CACHE_KEY_${cacheName}`
 | 
			
		||||
        this.cacheResultStateKey = `CACHE_RESULT_${cacheName}`
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async restore(): Promise<void> {
 | 
			
		||||
        if (this.cacheOutputExists()) {
 | 
			
		||||
            core.info(
 | 
			
		||||
                `${this.cacheDescription} already exists. Not restoring from cache.`
 | 
			
		||||
            )
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const cacheKey = generateCacheKey(this.cacheName)
 | 
			
		||||
 | 
			
		||||
        core.saveState(this.cacheKeyStateKey, cacheKey.key)
 | 
			
		||||
 | 
			
		||||
        const cacheResult = await cache.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)
 | 
			
		||||
 | 
			
		||||
        core.info(
 | 
			
		||||
            `${this.cacheDescription} restored from cache key: ${cacheResult}`
 | 
			
		||||
        )
 | 
			
		||||
        return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async save(): Promise<void> {
 | 
			
		||||
        if (!this.cacheOutputExists()) {
 | 
			
		||||
            core.debug(`No ${this.cacheDescription} to cache.`)
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const cacheKey = core.getState(this.cacheKeyStateKey)
 | 
			
		||||
        const cacheResult = core.getState(this.cacheResultStateKey)
 | 
			
		||||
 | 
			
		||||
        if (!cacheKey) {
 | 
			
		||||
            core.info(
 | 
			
		||||
                `${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
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        core.info(
 | 
			
		||||
            `Caching ${this.cacheDescription} with cache key: ${cacheKey}`
 | 
			
		||||
        )
 | 
			
		||||
        try {
 | 
			
		||||
            await cache.saveCache(this.getCachePath(), cacheKey)
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            // Fail on validation errors or non-errors (the latter to keep Typescript happy)
 | 
			
		||||
            if (
 | 
			
		||||
                error instanceof cache.ValidationError ||
 | 
			
		||||
                !(error instanceof Error)
 | 
			
		||||
            ) {
 | 
			
		||||
                throw error
 | 
			
		||||
            }
 | 
			
		||||
            core.warning(error.message)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected abstract cacheOutputExists(): boolean
 | 
			
		||||
    protected abstract getCachePath(): string[]
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import * as cacheGradleUserHome from './cache-gradle-user-home'
 | 
			
		||||
import * as cacheProjectDotGradle from './cache-project-dot-gradle'
 | 
			
		||||
import {GradleUserHomeCache} from './cache-gradle-user-home'
 | 
			
		||||
import {ProjectDotGradleCache} from './cache-project-dot-gradle'
 | 
			
		||||
import * as core from '@actions/core'
 | 
			
		||||
import {isCacheReadEnabled, isCacheSaveEnabled} from './cache-utils'
 | 
			
		||||
 | 
			
		||||
@@ -12,9 +12,9 @@ export async function restore(buildRootDirectory: string): Promise<void> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    core.startGroup('Restore Gradle state from cache')
 | 
			
		||||
    await cacheGradleUserHome.restore()
 | 
			
		||||
    core.saveState(BUILD_ROOT_DIR, buildRootDirectory)
 | 
			
		||||
    await cacheProjectDotGradle.restore(buildRootDirectory)
 | 
			
		||||
    new GradleUserHomeCache().restore()
 | 
			
		||||
    new ProjectDotGradleCache(buildRootDirectory).restore()
 | 
			
		||||
    core.endGroup()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -25,8 +25,8 @@ export async function save(): Promise<void> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    core.startGroup('Caching Gradle state')
 | 
			
		||||
    await cacheGradleUserHome.save()
 | 
			
		||||
    const buildRootDirectory = core.getState(BUILD_ROOT_DIR)
 | 
			
		||||
    await cacheProjectDotGradle.save(buildRootDirectory)
 | 
			
		||||
    new GradleUserHomeCache().save()
 | 
			
		||||
    new ProjectDotGradleCache(buildRootDirectory).save()
 | 
			
		||||
    core.endGroup()
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,9 @@ export async function run(): Promise<void> {
 | 
			
		||||
            core.setFailed(`Gradle process exited with status ${result.status}`)
 | 
			
		||||
        }
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
        if (!(error instanceof Error)) {
 | 
			
		||||
            throw error
 | 
			
		||||
        }
 | 
			
		||||
        core.setFailed(error.message)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -142,13 +142,14 @@ async function downloadAndCacheGradleDistribution(
 | 
			
		||||
        try {
 | 
			
		||||
            await cache.saveCache([downloadPath], cacheKey)
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            if (error.name === cache.ValidationError.name) {
 | 
			
		||||
            // Fail on validation errors or non-errors (the latter to keep Typescript happy)
 | 
			
		||||
            if (
 | 
			
		||||
                error instanceof cache.ValidationError ||
 | 
			
		||||
                !(error instanceof Error)
 | 
			
		||||
            ) {
 | 
			
		||||
                throw error
 | 
			
		||||
            } else if (error.name === cache.ReserveCacheError.name) {
 | 
			
		||||
                core.info(error.message)
 | 
			
		||||
            } else {
 | 
			
		||||
                core.info(`[warning] ${error.message}`)
 | 
			
		||||
            }
 | 
			
		||||
            core.warning(error.message)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return downloadPath
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user