mirror of
https://github.com/gradle/gradle-build-action.git
synced 2024-11-22 17:12:51 +00: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:
parent
c44ebadf6f
commit
6d1455a33e
6 changed files with 138 additions and 166 deletions
|
@ -2,86 +2,26 @@ import path from 'path'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import os from 'os'
|
import os from 'os'
|
||||||
|
|
||||||
import * as core from '@actions/core'
|
import {AbstractCache} from './cache-utils'
|
||||||
import * as cache from '@actions/cache'
|
|
||||||
import {generateCacheKey} from './cache-utils'
|
|
||||||
|
|
||||||
const CACHE_PATH = [
|
const CACHE_PATH = [
|
||||||
'~/.gradle/caches/*', // All directories in 'caches'
|
'~/.gradle/caches/*', // All directories in 'caches'
|
||||||
'~/.gradle/notifications/*', // Prevent the re-rendering of first-use message for version
|
'~/.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
|
'~/.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> {
|
export class GradleUserHomeCache extends AbstractCache {
|
||||||
if (gradleUserHomeExists()) {
|
constructor() {
|
||||||
core.info('Gradle User Home already exists. Not restoring from cache.')
|
super('gradle', 'Gradle User Home')
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const cacheKey = generateCacheKey('gradle')
|
protected cacheOutputExists(): boolean {
|
||||||
|
// Need to check for 'caches' directory to avoid incorrect detection on MacOS agents
|
||||||
core.saveState(CACHE_KEY, cacheKey.key)
|
const dir = path.resolve(os.homedir(), '.gradle/caches')
|
||||||
|
return fs.existsSync(dir)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
core.info(`Gradle User Home restored from cache key: ${cacheResult}`)
|
protected getCachePath(): string[] {
|
||||||
return
|
return CACHE_PATH
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,94 +1,29 @@
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
|
import {AbstractCache} from './cache-utils'
|
||||||
import * as core from '@actions/core'
|
|
||||||
import * as cache from '@actions/cache'
|
|
||||||
import {generateCacheKey} from './cache-utils'
|
|
||||||
|
|
||||||
const PATHS_TO_CACHE = [
|
const PATHS_TO_CACHE = [
|
||||||
'configuration-cache' // Only configuration-cache is stored at present
|
'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> {
|
export class ProjectDotGradleCache extends AbstractCache {
|
||||||
if (projectDotGradleDirExists(rootDir)) {
|
private rootDir: string
|
||||||
core.info(
|
constructor(rootDir: string) {
|
||||||
'Project .gradle directory already exists. Not restoring from cache.'
|
super('project', 'Project .gradle directory')
|
||||||
)
|
this.rootDir = rootDir
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const cacheKey = generateCacheKey('project')
|
protected cacheOutputExists(): boolean {
|
||||||
|
const dir = this.getProjectDotGradleDir()
|
||||||
core.saveState(CACHE_KEY, cacheKey.key)
|
return fs.existsSync(dir)
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
core.info(`Project .gradle dir restored from cache key: ${cacheResult}`)
|
protected getCachePath(): string[] {
|
||||||
return
|
const dir = this.getProjectDotGradleDir()
|
||||||
}
|
return PATHS_TO_CACHE.map(x => path.resolve(dir, x))
|
||||||
|
}
|
||||||
export async function save(rootDir: string): Promise<void> {
|
|
||||||
if (!projectDotGradleDirExists(rootDir)) {
|
private getProjectDotGradleDir(): string {
|
||||||
core.debug('No project .gradle dir to cache.')
|
return path.resolve(this.rootDir, '.gradle')
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
|
import * as cache from '@actions/cache'
|
||||||
import * as github from '@actions/github'
|
import * as github from '@actions/github'
|
||||||
import * as crypto from 'crypto'
|
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
|
// Prefix can be used to force change all cache keys
|
||||||
const cacheKeyPrefix = process.env['CACHE_KEY_PREFIX'] || ''
|
const cacheKeyPrefix = process.env['CACHE_KEY_PREFIX'] || ''
|
||||||
|
|
||||||
|
@ -70,7 +71,7 @@ export function hashStrings(values: string[]): string {
|
||||||
return hash.digest('hex')
|
return hash.digest('hex')
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CacheKey {
|
class CacheKey {
|
||||||
key: string
|
key: string
|
||||||
restoreKeys: string[]
|
restoreKeys: string[]
|
||||||
|
|
||||||
|
@ -79,3 +80,95 @@ export class CacheKey {
|
||||||
this.restoreKeys = restoreKeys
|
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 {GradleUserHomeCache} from './cache-gradle-user-home'
|
||||||
import * as cacheProjectDotGradle from './cache-project-dot-gradle'
|
import {ProjectDotGradleCache} from './cache-project-dot-gradle'
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import {isCacheReadEnabled, isCacheSaveEnabled} from './cache-utils'
|
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')
|
core.startGroup('Restore Gradle state from cache')
|
||||||
await cacheGradleUserHome.restore()
|
|
||||||
core.saveState(BUILD_ROOT_DIR, buildRootDirectory)
|
core.saveState(BUILD_ROOT_DIR, buildRootDirectory)
|
||||||
await cacheProjectDotGradle.restore(buildRootDirectory)
|
new GradleUserHomeCache().restore()
|
||||||
|
new ProjectDotGradleCache(buildRootDirectory).restore()
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,8 +25,8 @@ export async function save(): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
core.startGroup('Caching Gradle state')
|
core.startGroup('Caching Gradle state')
|
||||||
await cacheGradleUserHome.save()
|
|
||||||
const buildRootDirectory = core.getState(BUILD_ROOT_DIR)
|
const buildRootDirectory = core.getState(BUILD_ROOT_DIR)
|
||||||
await cacheProjectDotGradle.save(buildRootDirectory)
|
new GradleUserHomeCache().save()
|
||||||
|
new ProjectDotGradleCache(buildRootDirectory).save()
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,9 @@ export async function run(): Promise<void> {
|
||||||
core.setFailed(`Gradle process exited with status ${result.status}`)
|
core.setFailed(`Gradle process exited with status ${result.status}`)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (!(error instanceof Error)) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
core.setFailed(error.message)
|
core.setFailed(error.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -142,13 +142,14 @@ async function downloadAndCacheGradleDistribution(
|
||||||
try {
|
try {
|
||||||
await cache.saveCache([downloadPath], cacheKey)
|
await cache.saveCache([downloadPath], cacheKey)
|
||||||
} catch (error) {
|
} 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
|
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
|
return downloadPath
|
||||||
|
|
Loading…
Reference in a new issue