mirror of
https://github.com/gradle/gradle-build-action.git
synced 2024-11-22 17:12:51 +00:00
Generate cache key based on Job invocation
Attempt to capture as much context as possible about the job run to generate a unique cache key. Unfortunately much of the matrix context is not available to the action implementation.
This commit is contained in:
parent
d7ed6d7e8d
commit
777a6fc967
3 changed files with 47 additions and 33 deletions
2
.github/workflows/prod.yml
vendored
2
.github/workflows/prod.yml
vendored
|
@ -7,7 +7,7 @@ on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CACHE_KEY_SEED: ${{github.workflow}}#${{github.run_number}}-
|
CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# Run initial Gradle builds to push initial cache entries
|
# Run initial Gradle builds to push initial cache entries
|
||||||
|
|
|
@ -2,30 +2,14 @@ import * as cacheUtils from '../src/cache-utils'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
|
|
||||||
describe('cacheUtils-utils', () => {
|
describe('cacheUtils-utils', () => {
|
||||||
describe('can truncate args', () => {
|
describe('can hash', () => {
|
||||||
test('handles zero-length string', () => {
|
it('a string', async () => {
|
||||||
expect(cacheUtils.truncateArgs('')).toBe('')
|
const hash = cacheUtils.hashStrings(['foo'])
|
||||||
|
expect(hash).toBe('acbd18db4cc2f85cedef654fccc4a4d8')
|
||||||
})
|
})
|
||||||
test('leaves short string untouched', () => {
|
it('multiple strings', async () => {
|
||||||
expect(
|
const hash = cacheUtils.hashStrings(['foo', 'bar', 'baz'])
|
||||||
cacheUtils.truncateArgs('short string that-should-be-untouched')
|
expect(hash).toBe('6df23dc03f9b54cc38a0fc1483df6e21')
|
||||||
).toBe('short string that-should-be-untouched')
|
|
||||||
})
|
|
||||||
test('truncates long string', () => {
|
|
||||||
const longString = 'a'.repeat(500)
|
|
||||||
expect(cacheUtils.truncateArgs(longString)).toBe('a'.repeat(400))
|
|
||||||
})
|
|
||||||
test('trims leading and trailing whitespace', () => {
|
|
||||||
expect(cacheUtils.truncateArgs(' this is an arg ')).toBe(
|
|
||||||
'this is an arg'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
test('removes repeated whitespace', () => {
|
|
||||||
expect(
|
|
||||||
cacheUtils.truncateArgs(
|
|
||||||
' this one has long \t\n\t\r spaces '
|
|
||||||
)
|
|
||||||
).toBe('this one has long spaces')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as github from '@actions/github'
|
import * as github from '@actions/github'
|
||||||
|
import * as crypto from 'crypto'
|
||||||
|
|
||||||
export function isCacheReadEnabled(cacheName: string): boolean {
|
export function isCacheReadEnabled(cacheName: string): boolean {
|
||||||
const configValue = getCacheEnabledValue(cacheName)
|
const configValue = getCacheEnabledValue(cacheName)
|
||||||
|
@ -25,19 +26,48 @@ function getCacheEnabledValue(cacheName: string): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function generateCacheKey(cacheName: string): CacheKey {
|
export function generateCacheKey(cacheName: string): CacheKey {
|
||||||
const cacheKeySeed = process.env[`CACHE_KEY_SEED`] || ''
|
// Prefix can be used to force change all cache keys
|
||||||
const runnerOs = process.env[`RUNNER_OS`] || ''
|
const cacheKeyPrefix = process.env['CACHE_KEY_PREFIX'] || ''
|
||||||
const cacheKeyPrefix = `${cacheKeySeed}${runnerOs}|${cacheName}|`
|
|
||||||
|
|
||||||
const args = truncateArgs(core.getInput('arguments'))
|
// At the most general level, share caches for all executions on the same OS
|
||||||
const cacheKeyWithArgs = `${cacheKeyPrefix}${args}|`
|
const runnerOs = process.env['RUNNER_OS'] || ''
|
||||||
|
const cacheKeyForOs = `${cacheKeyPrefix}${cacheName}|${runnerOs}`
|
||||||
|
|
||||||
const cacheKey = `${cacheKeyWithArgs}${github.context.sha}`
|
// Prefer caches that run this job
|
||||||
return new CacheKey(cacheKey, [cacheKeyWithArgs, cacheKeyPrefix])
|
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 truncateArgs(args: string): string {
|
function determineJobContext(): string {
|
||||||
return args.trim().replace(/\s+/g, ' ').substr(0, 400)
|
// Ideally we'd serialize the entire matrix values here, but matrix is not available within the action invocation.
|
||||||
|
// Use the JAVA_HOME value as a proxy for the java version
|
||||||
|
const javaHome = process.env['JAVA_HOME'] || ''
|
||||||
|
|
||||||
|
// Approximate overall context based on the first gradle invocation in the Job
|
||||||
|
const args = core.getInput('arguments')
|
||||||
|
const buildRootDirectory = core.getInput('build-root-directory')
|
||||||
|
const gradleVersion = core.getInput('gradle-version')
|
||||||
|
|
||||||
|
return hashStrings([javaHome, args, buildRootDirectory, gradleVersion])
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hashStrings(values: string[]): string {
|
||||||
|
const hash = crypto.createHash('md5')
|
||||||
|
for (const value of values) {
|
||||||
|
hash.update(value)
|
||||||
|
}
|
||||||
|
return hash.digest('hex')
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CacheKey {
|
export class CacheKey {
|
||||||
|
|
Loading…
Reference in a new issue