mirror of
				https://github.com/gradle/gradle-build-action.git
				synced 2025-11-04 01:28:56 +08: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:
		
							
								
								
									
										2
									
								
								.github/workflows/prod.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/prod.yml
									
									
									
									
										vendored
									
									
								
							@@ -7,7 +7,7 @@ on:
 | 
			
		||||
  workflow_dispatch:
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  CACHE_KEY_SEED: ${{github.workflow}}#${{github.run_number}}-
 | 
			
		||||
  CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  # 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'
 | 
			
		||||
 | 
			
		||||
describe('cacheUtils-utils', () => {
 | 
			
		||||
    describe('can truncate args', () => {
 | 
			
		||||
        test('handles zero-length string', () => {
 | 
			
		||||
            expect(cacheUtils.truncateArgs('')).toBe('')
 | 
			
		||||
    describe('can hash', () => {
 | 
			
		||||
        it('a string', async () => {
 | 
			
		||||
            const hash = cacheUtils.hashStrings(['foo'])
 | 
			
		||||
            expect(hash).toBe('acbd18db4cc2f85cedef654fccc4a4d8')
 | 
			
		||||
        })
 | 
			
		||||
        test('leaves short string untouched', () => {
 | 
			
		||||
            expect(
 | 
			
		||||
                cacheUtils.truncateArgs('short string that-should-be-untouched')
 | 
			
		||||
            ).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')
 | 
			
		||||
        it('multiple strings', async () => {
 | 
			
		||||
            const hash = cacheUtils.hashStrings(['foo', 'bar', 'baz'])
 | 
			
		||||
            expect(hash).toBe('6df23dc03f9b54cc38a0fc1483df6e21')
 | 
			
		||||
        })
 | 
			
		||||
    })
 | 
			
		||||
})
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
import * as core from '@actions/core'
 | 
			
		||||
import * as github from '@actions/github'
 | 
			
		||||
import * as crypto from 'crypto'
 | 
			
		||||
 | 
			
		||||
export function isCacheReadEnabled(cacheName: string): boolean {
 | 
			
		||||
    const configValue = getCacheEnabledValue(cacheName)
 | 
			
		||||
@@ -25,19 +26,48 @@ function getCacheEnabledValue(cacheName: string): string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function generateCacheKey(cacheName: string): CacheKey {
 | 
			
		||||
    const cacheKeySeed = process.env[`CACHE_KEY_SEED`] || ''
 | 
			
		||||
    const runnerOs = process.env[`RUNNER_OS`] || ''
 | 
			
		||||
    const cacheKeyPrefix = `${cacheKeySeed}${runnerOs}|${cacheName}|`
 | 
			
		||||
    // Prefix can be used to force change all cache keys
 | 
			
		||||
    const cacheKeyPrefix = process.env['CACHE_KEY_PREFIX'] || ''
 | 
			
		||||
 | 
			
		||||
    const args = truncateArgs(core.getInput('arguments'))
 | 
			
		||||
    const cacheKeyWithArgs = `${cacheKeyPrefix}${args}|`
 | 
			
		||||
    // At the most general level, share caches for all executions on the same OS
 | 
			
		||||
    const runnerOs = process.env['RUNNER_OS'] || ''
 | 
			
		||||
    const cacheKeyForOs = `${cacheKeyPrefix}${cacheName}|${runnerOs}`
 | 
			
		||||
 | 
			
		||||
    const cacheKey = `${cacheKeyWithArgs}${github.context.sha}`
 | 
			
		||||
    return new CacheKey(cacheKey, [cacheKeyWithArgs, cacheKeyPrefix])
 | 
			
		||||
    // Prefer caches that run this job
 | 
			
		||||
    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 {
 | 
			
		||||
    return args.trim().replace(/\s+/g, ' ').substr(0, 400)
 | 
			
		||||
function determineJobContext(): string {
 | 
			
		||||
    // 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 {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user