mirror of
				https://github.com/gradle/gradle-build-action.git
				synced 2025-11-01 07:38:55 +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: |   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 { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user