mirror of
https://github.com/gradle/gradle-build-action
synced 2024-11-23 18:12:15 +00:00
Add experimental support for 'cache-write-only'
There may be cases where it a "fresh" cache entry would be beneficial, for example if the Gradle User Home cache entry grows over time. This setting would run the build as if no prior cache entry exists.
This commit is contained in:
parent
0a5ede19a9
commit
08d5b40ca5
6 changed files with 69 additions and 19 deletions
39
.github/workflows/integTest-caching-config.yml
vendored
39
.github/workflows/integTest-caching-config.yml
vendored
|
@ -101,3 +101,42 @@ jobs:
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
core.setFailed('No build scan detected')
|
core.setFailed('No build scan detected')
|
||||||
|
|
||||||
|
# Test seed the cache with cache-write-only and verify with cache-read-only
|
||||||
|
seed-build-write-only:
|
||||||
|
env:
|
||||||
|
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}:${{github.run_attempt}}-write-only-
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
cache-write-only: true
|
||||||
|
- name: Build using Gradle wrapper
|
||||||
|
working-directory: __tests__/samples/groovy-dsl
|
||||||
|
run: ./gradlew test
|
||||||
|
|
||||||
|
verify-write-only-build:
|
||||||
|
env:
|
||||||
|
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}:${{github.run_attempt}}-write-only-
|
||||||
|
needs: seed-build-write-only
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
cache-read-only: true
|
||||||
|
- name: Execute Gradle build with --offline
|
||||||
|
working-directory: __tests__/samples/groovy-dsl
|
||||||
|
run: ./gradlew test --offline
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,10 @@ inputs:
|
||||||
# The following action properties allow fine-grained tweaking of the action caching behaviour.
|
# The following action properties allow fine-grained tweaking of the action caching behaviour.
|
||||||
# These properties are experimental and not (yet) designed for production use, and may change without notice in a subsequent release of `gradle-build-action`.
|
# These properties are experimental and not (yet) designed for production use, and may change without notice in a subsequent release of `gradle-build-action`.
|
||||||
# Use at your own risk!
|
# Use at your own risk!
|
||||||
|
cache-write-only:
|
||||||
|
description: When 'true', entries will not be restored from the cache but will be saved at the end of the Job. This allows a 'clean' cache entry to be written.
|
||||||
|
required: false
|
||||||
|
default: false
|
||||||
gradle-home-cache-strict-match:
|
gradle-home-cache-strict-match:
|
||||||
description: When 'true', the action will not attempt to restore the Gradle User Home entries from other Jobs.
|
description: When 'true', the action will not attempt to restore the Gradle User Home entries from other Jobs.
|
||||||
required: false
|
required: false
|
||||||
|
|
|
@ -16,6 +16,7 @@ import {
|
||||||
import {ConfigurationCacheEntryExtractor, GradleHomeEntryExtractor} from './cache-extract-entries'
|
import {ConfigurationCacheEntryExtractor, GradleHomeEntryExtractor} from './cache-extract-entries'
|
||||||
|
|
||||||
const CACHE_PROTOCOL_VERSION = 'v6-'
|
const CACHE_PROTOCOL_VERSION = 'v6-'
|
||||||
|
const RESTORED_CACHE_KEY_KEY = 'restored-cache-key'
|
||||||
|
|
||||||
export const META_FILE_DIR = '.gradle-build-action'
|
export const META_FILE_DIR = '.gradle-build-action'
|
||||||
export const PROJECT_ROOTS_FILE = 'project-roots.txt'
|
export const PROJECT_ROOTS_FILE = 'project-roots.txt'
|
||||||
|
@ -81,8 +82,6 @@ function generateCacheKey(cacheName: string): CacheKey {
|
||||||
export class GradleStateCache {
|
export class GradleStateCache {
|
||||||
private cacheName: string
|
private cacheName: string
|
||||||
private cacheDescription: string
|
private cacheDescription: string
|
||||||
private cacheKeyStateKey: string
|
|
||||||
private cacheResultStateKey: string
|
|
||||||
|
|
||||||
protected readonly gradleUserHome: string
|
protected readonly gradleUserHome: string
|
||||||
|
|
||||||
|
@ -90,8 +89,6 @@ export class GradleStateCache {
|
||||||
this.gradleUserHome = gradleUserHome
|
this.gradleUserHome = gradleUserHome
|
||||||
this.cacheName = 'gradle'
|
this.cacheName = 'gradle'
|
||||||
this.cacheDescription = 'Gradle User Home'
|
this.cacheDescription = 'Gradle User Home'
|
||||||
this.cacheKeyStateKey = `CACHE_KEY_gradle`
|
|
||||||
this.cacheResultStateKey = `CACHE_RESULT_gradle`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init(): void {
|
init(): void {
|
||||||
|
@ -122,7 +119,6 @@ export class GradleStateCache {
|
||||||
const entryListener = listener.entry(this.cacheDescription)
|
const entryListener = listener.entry(this.cacheDescription)
|
||||||
|
|
||||||
const cacheKey = generateCacheKey(this.cacheName)
|
const cacheKey = generateCacheKey(this.cacheName)
|
||||||
core.saveState(this.cacheKeyStateKey, cacheKey.key)
|
|
||||||
|
|
||||||
cacheDebug(
|
cacheDebug(
|
||||||
`Requesting ${this.cacheDescription} with
|
`Requesting ${this.cacheDescription} with
|
||||||
|
@ -136,7 +132,7 @@ export class GradleStateCache {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
core.saveState(this.cacheResultStateKey, cacheResult.key)
|
core.saveState(RESTORED_CACHE_KEY_KEY, cacheResult.key)
|
||||||
|
|
||||||
core.info(`Restored ${this.cacheDescription} from cache key: ${cacheResult.key}`)
|
core.info(`Restored ${this.cacheDescription} from cache key: ${cacheResult.key}`)
|
||||||
|
|
||||||
|
@ -165,12 +161,11 @@ export class GradleStateCache {
|
||||||
* 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> {
|
||||||
// Retrieve the state set in the previous 'restore' step.
|
const cacheKey = generateCacheKey(this.cacheName).key
|
||||||
const cacheKeyFromRestore = core.getState(this.cacheKeyStateKey)
|
const restoredCacheKey = core.getState(RESTORED_CACHE_KEY_KEY)
|
||||||
const cacheResultFromRestore = core.getState(this.cacheResultStateKey)
|
|
||||||
|
|
||||||
if (cacheResultFromRestore && cacheKeyFromRestore === cacheResultFromRestore) {
|
if (restoredCacheKey && cacheKey === restoredCacheKey) {
|
||||||
core.info(`Cache hit occurred on the cache key ${cacheKeyFromRestore}, not saving cache.`)
|
core.info(`Cache hit occurred on the cache key ${cacheKey}, not saving cache.`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,10 +176,10 @@ export class GradleStateCache {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
core.info(`Caching ${this.cacheDescription} with cache key: ${cacheKeyFromRestore}`)
|
core.info(`Caching ${this.cacheDescription} with cache key: ${cacheKey}`)
|
||||||
const cachePath = this.getCachePath()
|
const cachePath = this.getCachePath()
|
||||||
const entryListener = listener.entry(this.cacheDescription)
|
const entryListener = listener.entry(this.cacheDescription)
|
||||||
await saveCache(cachePath, cacheKeyFromRestore, entryListener)
|
await saveCache(cachePath, cacheKey, entryListener)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,9 @@ export class CacheListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
static rehydrate(stringRep: string): CacheListener {
|
static rehydrate(stringRep: string): CacheListener {
|
||||||
|
if (stringRep === '') {
|
||||||
|
return new CacheListener()
|
||||||
|
}
|
||||||
const rehydrated: CacheListener = Object.assign(new CacheListener(), JSON.parse(stringRep))
|
const rehydrated: CacheListener = Object.assign(new CacheListener(), JSON.parse(stringRep))
|
||||||
const entries = rehydrated.cacheEntries
|
const entries = rehydrated.cacheEntries
|
||||||
for (let index = 0; index < entries.length; index++) {
|
for (let index = 0; index < entries.length; index++) {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {CacheEntryListener} from './cache-reporting'
|
||||||
const JOB_CONTEXT_PARAMETER = 'workflow-job-context'
|
const JOB_CONTEXT_PARAMETER = 'workflow-job-context'
|
||||||
const CACHE_DISABLED_PARAMETER = 'cache-disabled'
|
const CACHE_DISABLED_PARAMETER = 'cache-disabled'
|
||||||
const CACHE_READONLY_PARAMETER = 'cache-read-only'
|
const CACHE_READONLY_PARAMETER = 'cache-read-only'
|
||||||
|
const CACHE_WRITEONLY_PARAMETER = 'cache-write-only'
|
||||||
const CACHE_DEBUG_VAR = 'GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED'
|
const CACHE_DEBUG_VAR = 'GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED'
|
||||||
const CACHE_PREFIX_VAR = 'GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX'
|
const CACHE_PREFIX_VAR = 'GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX'
|
||||||
|
|
||||||
|
@ -20,6 +21,10 @@ export function isCacheReadOnly(): boolean {
|
||||||
return core.getBooleanInput(CACHE_READONLY_PARAMETER)
|
return core.getBooleanInput(CACHE_READONLY_PARAMETER)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isCacheWriteOnly(): boolean {
|
||||||
|
return core.getBooleanInput(CACHE_WRITEONLY_PARAMETER)
|
||||||
|
}
|
||||||
|
|
||||||
export function isCacheDebuggingEnabled(): boolean {
|
export function isCacheDebuggingEnabled(): boolean {
|
||||||
return process.env[CACHE_DEBUG_VAR] ? true : false
|
return process.env[CACHE_DEBUG_VAR] ? true : false
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import {isCacheDisabled, isCacheReadOnly} from './cache-utils'
|
import {isCacheDisabled, isCacheReadOnly, isCacheWriteOnly} from './cache-utils'
|
||||||
import {logCachingReport, CacheListener} from './cache-reporting'
|
import {logCachingReport, CacheListener} from './cache-reporting'
|
||||||
import {GradleStateCache} from './cache-base'
|
import {GradleStateCache} from './cache-base'
|
||||||
|
|
||||||
|
@ -32,18 +32,22 @@ export async function restore(gradleUserHome: string): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
gradleStateCache.init()
|
gradleStateCache.init()
|
||||||
|
// Mark the state as restored so that post-action will perform save.
|
||||||
|
core.saveState(CACHE_RESTORED_VAR, true)
|
||||||
|
// Save the Gradle User Home for the post-action step.
|
||||||
|
core.saveState(GRADLE_USER_HOME, gradleUserHome)
|
||||||
|
|
||||||
|
if (isCacheWriteOnly()) {
|
||||||
|
core.info('Cache is write-only: will not restore from cache.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
await core.group('Restore Gradle state from cache', async () => {
|
await core.group('Restore Gradle state from cache', async () => {
|
||||||
core.saveState(GRADLE_USER_HOME, gradleUserHome)
|
|
||||||
|
|
||||||
const cacheListener = new CacheListener()
|
const cacheListener = new CacheListener()
|
||||||
await gradleStateCache.restore(cacheListener)
|
await gradleStateCache.restore(cacheListener)
|
||||||
|
|
||||||
core.saveState(CACHE_LISTENER, cacheListener.stringify())
|
core.saveState(CACHE_LISTENER, cacheListener.stringify())
|
||||||
})
|
})
|
||||||
|
|
||||||
// Export state that is detected in corresponding post-action step
|
|
||||||
core.saveState(CACHE_RESTORED_VAR, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function save(): Promise<void> {
|
export async function save(): Promise<void> {
|
||||||
|
|
Loading…
Reference in a new issue