mirror of
				https://github.com/gradle/gradle-build-action.git
				synced 2025-11-04 09:58:56 +08:00 
			
		
		
		
	Refactor: extract cache-base out of cache-utils
This commit is contained in:
		
							
								
								
									
										95
									
								
								__tests__/cache-base.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								__tests__/cache-base.test.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,95 @@
 | 
				
			|||||||
 | 
					import {CacheEntryReport, CachingReport} from '../src/cache-base'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('caching report', () => {
 | 
				
			||||||
 | 
					    describe('reports not fully restored', () => {
 | 
				
			||||||
 | 
					        it('with one requested entry report', async () => {
 | 
				
			||||||
 | 
					            const report = new CachingReport()
 | 
				
			||||||
 | 
					            report.entryReport('foo').markRequested('1', ['2'])
 | 
				
			||||||
 | 
					            report.entryReport('bar').markRequested('3').markRestored('4')
 | 
				
			||||||
 | 
					            expect(report.fullyRestored).toBe(false)
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    describe('reports fully restored', () => {
 | 
				
			||||||
 | 
					        it('when empty', async () => {
 | 
				
			||||||
 | 
					            const report = new CachingReport()
 | 
				
			||||||
 | 
					            expect(report.fullyRestored).toBe(true)
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        it('with empty entry reports', async () => {
 | 
				
			||||||
 | 
					            const report = new CachingReport()
 | 
				
			||||||
 | 
					            report.entryReport('foo')
 | 
				
			||||||
 | 
					            report.entryReport('bar')
 | 
				
			||||||
 | 
					            expect(report.fullyRestored).toBe(true)
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        it('with restored entry report', async () => {
 | 
				
			||||||
 | 
					            const report = new CachingReport()
 | 
				
			||||||
 | 
					            report.entryReport('bar').markRequested('3').markRestored('4')
 | 
				
			||||||
 | 
					            expect(report.fullyRestored).toBe(true)
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        it('with multiple restored entry reportss', async () => {
 | 
				
			||||||
 | 
					            const report = new CachingReport()
 | 
				
			||||||
 | 
					            report.entryReport('foo').markRestored('4')
 | 
				
			||||||
 | 
					            report.entryReport('bar').markRequested('3').markRestored('4')
 | 
				
			||||||
 | 
					            expect(report.fullyRestored).toBe(true)
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    describe('can be stringified and rehydrated', () => {
 | 
				
			||||||
 | 
					        it('when empty', async () => {
 | 
				
			||||||
 | 
					            const report = new CachingReport()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const stringRep = report.stringify()
 | 
				
			||||||
 | 
					            const reportClone: CachingReport = CachingReport.rehydrate(stringRep)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            expect(reportClone.cacheEntryReports).toEqual([])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Can call methods on rehydrated
 | 
				
			||||||
 | 
					            expect(reportClone.entryReport('foo')).toBeInstanceOf(CacheEntryReport)
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        it('with entry reports', async () => {
 | 
				
			||||||
 | 
					            const report = new CachingReport()
 | 
				
			||||||
 | 
					            report.entryReport('foo')
 | 
				
			||||||
 | 
					            report.entryReport('bar')
 | 
				
			||||||
 | 
					            report.entryReport('baz')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const stringRep = report.stringify()
 | 
				
			||||||
 | 
					            const reportClone: CachingReport = CachingReport.rehydrate(stringRep)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            expect(reportClone.cacheEntryReports.length).toBe(3)
 | 
				
			||||||
 | 
					            expect(reportClone.cacheEntryReports[0].entryName).toBe('foo')
 | 
				
			||||||
 | 
					            expect(reportClone.cacheEntryReports[1].entryName).toBe('bar')
 | 
				
			||||||
 | 
					            expect(reportClone.cacheEntryReports[2].entryName).toBe('baz')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            expect(reportClone.entryReport('foo')).toBe(reportClone.cacheEntryReports[0])
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        it('with rehydrated entry report', async () => {
 | 
				
			||||||
 | 
					            const report = new CachingReport()
 | 
				
			||||||
 | 
					            const entryReport = report.entryReport('foo')
 | 
				
			||||||
 | 
					            entryReport.markRequested('1', ['2', '3'])
 | 
				
			||||||
 | 
					            entryReport.markSaved('4')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const stringRep = report.stringify()
 | 
				
			||||||
 | 
					            const reportClone: CachingReport = CachingReport.rehydrate(stringRep)
 | 
				
			||||||
 | 
					            const entryClone = reportClone.entryReport('foo')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            expect(entryClone.requestedKey).toBe('1')
 | 
				
			||||||
 | 
					            expect(entryClone.requestedRestoreKeys).toEqual(['2', '3'])
 | 
				
			||||||
 | 
					            expect(entryClone.savedKey).toBe('4')
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        it('with live entry report', async () => {
 | 
				
			||||||
 | 
					            const report = new CachingReport()
 | 
				
			||||||
 | 
					            const entryReport = report.entryReport('foo')
 | 
				
			||||||
 | 
					            entryReport.markRequested('1', ['2', '3'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const stringRep = report.stringify()
 | 
				
			||||||
 | 
					            const reportClone: CachingReport = CachingReport.rehydrate(stringRep)
 | 
				
			||||||
 | 
					            const entryClone = reportClone.entryReport('foo')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Check type and call method on rehydrated entry report
 | 
				
			||||||
 | 
					            expect(entryClone).toBeInstanceOf(CacheEntryReport)
 | 
				
			||||||
 | 
					            entryClone.markSaved('4')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            expect(entryClone.requestedKey).toBe('1')
 | 
				
			||||||
 | 
					            expect(entryClone.requestedRestoreKeys).toEqual(['2', '3'])
 | 
				
			||||||
 | 
					            expect(entryClone.savedKey).toBe('4')
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
@@ -17,97 +17,4 @@ describe('cacheUtils-utils', () => {
 | 
				
			|||||||
            expect(posixHash).toBe(windowsHash)
 | 
					            expect(posixHash).toBe(windowsHash)
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    describe('caching report', () => {
 | 
					 | 
				
			||||||
        describe('reports not fully restored', () => {
 | 
					 | 
				
			||||||
            it('with one requested entry report', async () => {
 | 
					 | 
				
			||||||
                const report = new cacheUtils.CachingReport()
 | 
					 | 
				
			||||||
                report.entryReport('foo').markRequested('1', ['2'])
 | 
					 | 
				
			||||||
                report.entryReport('bar').markRequested('3').markRestored('4')
 | 
					 | 
				
			||||||
                expect(report.fullyRestored).toBe(false)
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        describe('reports fully restored', () => {
 | 
					 | 
				
			||||||
            it('when empty', async () => {
 | 
					 | 
				
			||||||
                const report = new cacheUtils.CachingReport()
 | 
					 | 
				
			||||||
                expect(report.fullyRestored).toBe(true)
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
            it('with empty entry reports', async () => {
 | 
					 | 
				
			||||||
                const report = new cacheUtils.CachingReport()
 | 
					 | 
				
			||||||
                report.entryReport('foo')
 | 
					 | 
				
			||||||
                report.entryReport('bar')
 | 
					 | 
				
			||||||
                expect(report.fullyRestored).toBe(true)
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
            it('with restored entry report', async () => {
 | 
					 | 
				
			||||||
                const report = new cacheUtils.CachingReport()
 | 
					 | 
				
			||||||
                report.entryReport('bar').markRequested('3').markRestored('4')
 | 
					 | 
				
			||||||
                expect(report.fullyRestored).toBe(true)
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
            it('with multiple restored entry reportss', async () => {
 | 
					 | 
				
			||||||
                const report = new cacheUtils.CachingReport()
 | 
					 | 
				
			||||||
                report.entryReport('foo').markRestored('4')
 | 
					 | 
				
			||||||
                report.entryReport('bar').markRequested('3').markRestored('4')
 | 
					 | 
				
			||||||
                expect(report.fullyRestored).toBe(true)
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        describe('can be stringified and rehydrated', () => {
 | 
					 | 
				
			||||||
            it('when empty', async () => {
 | 
					 | 
				
			||||||
                const report = new cacheUtils.CachingReport()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                const stringRep = report.stringify()
 | 
					 | 
				
			||||||
                const reportClone: cacheUtils.CachingReport = cacheUtils.CachingReport.rehydrate(stringRep)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                expect(reportClone.cacheEntryReports).toEqual([])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // Can call methods on rehydrated
 | 
					 | 
				
			||||||
                expect(reportClone.entryReport('foo')).toBeInstanceOf(cacheUtils.CacheEntryReport)
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
            it('with entry reports', async () => {
 | 
					 | 
				
			||||||
                const report = new cacheUtils.CachingReport()
 | 
					 | 
				
			||||||
                report.entryReport('foo')
 | 
					 | 
				
			||||||
                report.entryReport('bar')
 | 
					 | 
				
			||||||
                report.entryReport('baz')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                const stringRep = report.stringify()
 | 
					 | 
				
			||||||
                const reportClone: cacheUtils.CachingReport = cacheUtils.CachingReport.rehydrate(stringRep)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                expect(reportClone.cacheEntryReports.length).toBe(3)
 | 
					 | 
				
			||||||
                expect(reportClone.cacheEntryReports[0].entryName).toBe('foo')
 | 
					 | 
				
			||||||
                expect(reportClone.cacheEntryReports[1].entryName).toBe('bar')
 | 
					 | 
				
			||||||
                expect(reportClone.cacheEntryReports[2].entryName).toBe('baz')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                expect(reportClone.entryReport('foo')).toBe(reportClone.cacheEntryReports[0])
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
            it('with rehydrated entry report', async () => {
 | 
					 | 
				
			||||||
                const report = new cacheUtils.CachingReport()
 | 
					 | 
				
			||||||
                const entryReport = report.entryReport('foo')
 | 
					 | 
				
			||||||
                entryReport.markRequested('1', ['2', '3'])
 | 
					 | 
				
			||||||
                entryReport.markSaved('4')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                const stringRep = report.stringify()
 | 
					 | 
				
			||||||
                const reportClone: cacheUtils.CachingReport = cacheUtils.CachingReport.rehydrate(stringRep)
 | 
					 | 
				
			||||||
                const entryClone = reportClone.entryReport('foo')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                expect(entryClone.requestedKey).toBe('1')
 | 
					 | 
				
			||||||
                expect(entryClone.requestedRestoreKeys).toEqual(['2', '3'])
 | 
					 | 
				
			||||||
                expect(entryClone.savedKey).toBe('4')
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
            it('with live entry report', async () => {
 | 
					 | 
				
			||||||
                const report = new cacheUtils.CachingReport()
 | 
					 | 
				
			||||||
                const entryReport = report.entryReport('foo')
 | 
					 | 
				
			||||||
                entryReport.markRequested('1', ['2', '3'])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                const stringRep = report.stringify()
 | 
					 | 
				
			||||||
                const reportClone: cacheUtils.CachingReport = cacheUtils.CachingReport.rehydrate(stringRep)
 | 
					 | 
				
			||||||
                const entryClone = reportClone.entryReport('foo')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // Check type and call method on rehydrated entry report
 | 
					 | 
				
			||||||
                expect(entryClone).toBeInstanceOf(cacheUtils.CacheEntryReport)
 | 
					 | 
				
			||||||
                entryClone.markSaved('4')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                expect(entryClone.requestedKey).toBe('1')
 | 
					 | 
				
			||||||
                expect(entryClone.requestedRestoreKeys).toEqual(['2', '3'])
 | 
					 | 
				
			||||||
                expect(entryClone.savedKey).toBe('4')
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										253
									
								
								src/cache-base.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										253
									
								
								src/cache-base.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,253 @@
 | 
				
			|||||||
 | 
					import * as core from '@actions/core'
 | 
				
			||||||
 | 
					import * as cache from '@actions/cache'
 | 
				
			||||||
 | 
					import * as github from '@actions/github'
 | 
				
			||||||
 | 
					import {isCacheDebuggingEnabled, getCacheKeyPrefix, hashStrings} from './cache-utils'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const JOB_CONTEXT_PARAMETER = 'workflow-job-context'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function generateCacheKey(cacheName: string): CacheKey {
 | 
				
			||||||
 | 
					    const cacheKeyPrefix = getCacheKeyPrefix()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 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}`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 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 determineJobContext(): string {
 | 
				
			||||||
 | 
					    // By default, we hash the full `matrix` data for the run, to uniquely identify this job invocation
 | 
				
			||||||
 | 
					    const workflowJobContext = core.getInput(JOB_CONTEXT_PARAMETER)
 | 
				
			||||||
 | 
					    return hashStrings([workflowJobContext])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CacheKey {
 | 
				
			||||||
 | 
					    key: string
 | 
				
			||||||
 | 
					    restoreKeys: string[]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(key: string, restoreKeys: string[]) {
 | 
				
			||||||
 | 
					        this.key = key
 | 
				
			||||||
 | 
					        this.restoreKeys = restoreKeys
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class CachingReport {
 | 
				
			||||||
 | 
					    cacheEntryReports: CacheEntryReport[] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    get fullyRestored(): boolean {
 | 
				
			||||||
 | 
					        return this.cacheEntryReports.every(x => !x.wasRequestedButNotRestored())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    entryReport(name: string): CacheEntryReport {
 | 
				
			||||||
 | 
					        for (const report of this.cacheEntryReports) {
 | 
				
			||||||
 | 
					            if (report.entryName === name) {
 | 
				
			||||||
 | 
					                return report
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const newReport = new CacheEntryReport(name)
 | 
				
			||||||
 | 
					        this.cacheEntryReports.push(newReport)
 | 
				
			||||||
 | 
					        return newReport
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    stringify(): string {
 | 
				
			||||||
 | 
					        return JSON.stringify(this)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static rehydrate(stringRep: string): CachingReport {
 | 
				
			||||||
 | 
					        const rehydrated: CachingReport = Object.assign(new CachingReport(), JSON.parse(stringRep))
 | 
				
			||||||
 | 
					        const entryReports = rehydrated.cacheEntryReports
 | 
				
			||||||
 | 
					        for (let index = 0; index < entryReports.length; index++) {
 | 
				
			||||||
 | 
					            const rawEntryReport = entryReports[index]
 | 
				
			||||||
 | 
					            entryReports[index] = Object.assign(new CacheEntryReport(rawEntryReport.entryName), rawEntryReport)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return rehydrated
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class CacheEntryReport {
 | 
				
			||||||
 | 
					    entryName: string
 | 
				
			||||||
 | 
					    requestedKey: string | undefined
 | 
				
			||||||
 | 
					    requestedRestoreKeys: string[] | undefined
 | 
				
			||||||
 | 
					    restoredKey: string | undefined
 | 
				
			||||||
 | 
					    restoredSize: number | undefined
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    savedKey: string | undefined
 | 
				
			||||||
 | 
					    savedSize: number | undefined
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(entryName: string) {
 | 
				
			||||||
 | 
					        this.entryName = entryName
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    wasRequestedButNotRestored(): boolean {
 | 
				
			||||||
 | 
					        return this.requestedKey !== undefined && this.restoredKey === undefined
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    markRequested(key: string, restoreKeys: string[] = []): CacheEntryReport {
 | 
				
			||||||
 | 
					        this.requestedKey = key
 | 
				
			||||||
 | 
					        this.requestedRestoreKeys = restoreKeys
 | 
				
			||||||
 | 
					        return this
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    markRestored(key: string): CacheEntryReport {
 | 
				
			||||||
 | 
					        this.restoredKey = key
 | 
				
			||||||
 | 
					        return this
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    markSaved(key: string): CacheEntryReport {
 | 
				
			||||||
 | 
					        this.savedKey = key
 | 
				
			||||||
 | 
					        return this
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export abstract class AbstractCache {
 | 
				
			||||||
 | 
					    private cacheName: string
 | 
				
			||||||
 | 
					    private cacheDescription: string
 | 
				
			||||||
 | 
					    private cacheKeyStateKey: string
 | 
				
			||||||
 | 
					    private cacheResultStateKey: string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected readonly cacheDebuggingEnabled: boolean
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(cacheName: string, cacheDescription: string) {
 | 
				
			||||||
 | 
					        this.cacheName = cacheName
 | 
				
			||||||
 | 
					        this.cacheDescription = cacheDescription
 | 
				
			||||||
 | 
					        this.cacheKeyStateKey = `CACHE_KEY_${cacheName}`
 | 
				
			||||||
 | 
					        this.cacheResultStateKey = `CACHE_RESULT_${cacheName}`
 | 
				
			||||||
 | 
					        this.cacheDebuggingEnabled = isCacheDebuggingEnabled()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async restore(report: CachingReport): Promise<void> {
 | 
				
			||||||
 | 
					        if (this.cacheOutputExists()) {
 | 
				
			||||||
 | 
					            core.info(`${this.cacheDescription} already exists. Not restoring from cache.`)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const cacheKey = this.prepareCacheKey()
 | 
				
			||||||
 | 
					        const entryReport = report.entryReport(this.cacheName)
 | 
				
			||||||
 | 
					        entryReport.markRequested(cacheKey.key, cacheKey.restoreKeys)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.debug(
 | 
				
			||||||
 | 
					            `Requesting ${this.cacheDescription} with
 | 
				
			||||||
 | 
					                key:${cacheKey.key}
 | 
				
			||||||
 | 
					                restoreKeys:[${cacheKey.restoreKeys}]`
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const cacheResult = await this.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)
 | 
				
			||||||
 | 
					        entryReport.markRestored(cacheResult)
 | 
				
			||||||
 | 
					        core.info(`Restored ${this.cacheDescription} from cache key: ${cacheResult}`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            await this.afterRestore(report)
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            core.warning(`Restore ${this.cacheDescription} failed in 'afterRestore': ${error}`)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    prepareCacheKey(): CacheKey {
 | 
				
			||||||
 | 
					        const cacheKey = generateCacheKey(this.cacheName)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        core.saveState(this.cacheKeyStateKey, cacheKey.key)
 | 
				
			||||||
 | 
					        return cacheKey
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected async restoreCache(
 | 
				
			||||||
 | 
					        cachePath: string[],
 | 
				
			||||||
 | 
					        cacheKey: string,
 | 
				
			||||||
 | 
					        cacheRestoreKeys: string[] = []
 | 
				
			||||||
 | 
					    ): Promise<string | undefined> {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            return await cache.restoreCache(cachePath, cacheKey, cacheRestoreKeys)
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            if (error instanceof cache.ValidationError) {
 | 
				
			||||||
 | 
					                // Validation errors should fail the build action
 | 
				
			||||||
 | 
					                throw error
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            // Warn about any other error and continue
 | 
				
			||||||
 | 
					            core.warning(`Failed to restore ${cacheKey}: ${error}`)
 | 
				
			||||||
 | 
					            return undefined
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected async afterRestore(_report: CachingReport): Promise<void> {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async save(report: CachingReport): Promise<void> {
 | 
				
			||||||
 | 
					        if (!this.cacheOutputExists()) {
 | 
				
			||||||
 | 
					            this.debug(`No ${this.cacheDescription} to cache.`)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const cacheKey = core.getState(this.cacheKeyStateKey)
 | 
				
			||||||
 | 
					        const cacheResult = core.getState(this.cacheResultStateKey)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!cacheKey) {
 | 
				
			||||||
 | 
					            this.debug(`${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
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            await this.beforeSave(report)
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            core.warning(`Save ${this.cacheDescription} failed in 'beforeSave': ${error}`)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        core.info(`Caching ${this.cacheDescription} with cache key: ${cacheKey}`)
 | 
				
			||||||
 | 
					        const cachePath = this.getCachePath()
 | 
				
			||||||
 | 
					        await this.saveCache(cachePath, cacheKey)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        report.entryReport(this.cacheName).markSaved(cacheKey)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected async beforeSave(_report: CachingReport): Promise<void> {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected async saveCache(cachePath: string[], cacheKey: string): Promise<void> {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            await cache.saveCache(cachePath, cacheKey)
 | 
				
			||||||
 | 
					        } catch (error) {
 | 
				
			||||||
 | 
					            if (error instanceof cache.ValidationError) {
 | 
				
			||||||
 | 
					                // Validation errors should fail the build action
 | 
				
			||||||
 | 
					                throw error
 | 
				
			||||||
 | 
					            } else if (error instanceof cache.ReserveCacheError) {
 | 
				
			||||||
 | 
					                // Reserve cache errors are expected if the artifact has been previously cached
 | 
				
			||||||
 | 
					                this.debug(error.message)
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // Warn about any other error and continue
 | 
				
			||||||
 | 
					                core.warning(String(error))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected debug(message: string): void {
 | 
				
			||||||
 | 
					        if (this.cacheDebuggingEnabled) {
 | 
				
			||||||
 | 
					            core.info(message)
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            core.debug(message)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected abstract cacheOutputExists(): boolean
 | 
				
			||||||
 | 
					    protected abstract getCachePath(): string[]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -5,14 +5,8 @@ import * as core from '@actions/core'
 | 
				
			|||||||
import * as glob from '@actions/glob'
 | 
					import * as glob from '@actions/glob'
 | 
				
			||||||
import * as exec from '@actions/exec'
 | 
					import * as exec from '@actions/exec'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {
 | 
					import {AbstractCache, CacheEntryReport, CachingReport} from './cache-base'
 | 
				
			||||||
    AbstractCache,
 | 
					import {getCacheKeyPrefix, hashFileNames, tryDelete} from './cache-utils'
 | 
				
			||||||
    CacheEntryReport,
 | 
					 | 
				
			||||||
    CachingReport,
 | 
					 | 
				
			||||||
    getCacheKeyPrefix,
 | 
					 | 
				
			||||||
    hashFileNames,
 | 
					 | 
				
			||||||
    tryDelete
 | 
					 | 
				
			||||||
} from './cache-utils'
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const META_FILE_DIR = '.gradle-build-action'
 | 
					const META_FILE_DIR = '.gradle-build-action'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
import path from 'path'
 | 
					import path from 'path'
 | 
				
			||||||
import fs from 'fs'
 | 
					import fs from 'fs'
 | 
				
			||||||
import {AbstractCache} from './cache-utils'
 | 
					import {AbstractCache} from './cache-base'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: Maybe allow the user to override / tweak this set
 | 
					// TODO: Maybe allow the user to override / tweak this set
 | 
				
			||||||
const PATHS_TO_CACHE = [
 | 
					const PATHS_TO_CACHE = [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,4 @@
 | 
				
			|||||||
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 crypto from 'crypto'
 | 
					import * as crypto from 'crypto'
 | 
				
			||||||
import * as path from 'path'
 | 
					import * as path from 'path'
 | 
				
			||||||
import * as fs from 'fs'
 | 
					import * as fs from 'fs'
 | 
				
			||||||
@@ -9,7 +7,6 @@ const CACHE_PROTOCOL_VERSION = 'v4-'
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
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 JOB_CONTEXT_PARAMETER = 'workflow-job-context'
 | 
					 | 
				
			||||||
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'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -30,31 +27,6 @@ export function getCacheKeyPrefix(): string {
 | 
				
			|||||||
    return process.env[CACHE_PREFIX_VAR] || CACHE_PROTOCOL_VERSION
 | 
					    return process.env[CACHE_PREFIX_VAR] || CACHE_PROTOCOL_VERSION
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function generateCacheKey(cacheName: string): CacheKey {
 | 
					 | 
				
			||||||
    const cacheKeyPrefix = getCacheKeyPrefix()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 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}`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 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 determineJobContext(): string {
 | 
					 | 
				
			||||||
    // By default, we hash the full `matrix` data for the run, to uniquely identify this job invocation
 | 
					 | 
				
			||||||
    const workflowJobContext = core.getInput(JOB_CONTEXT_PARAMETER)
 | 
					 | 
				
			||||||
    return hashStrings([workflowJobContext])
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function hashStrings(values: string[]): string {
 | 
					export function hashStrings(values: string[]): string {
 | 
				
			||||||
    const hash = crypto.createHash('md5')
 | 
					    const hash = crypto.createHash('md5')
 | 
				
			||||||
    for (const value of values) {
 | 
					    for (const value of values) {
 | 
				
			||||||
@@ -94,225 +66,3 @@ export async function tryDelete(file: string): Promise<void> {
 | 
				
			|||||||
async function delay(ms: number): Promise<void> {
 | 
					async function delay(ms: number): Promise<void> {
 | 
				
			||||||
    return new Promise(resolve => setTimeout(resolve, ms))
 | 
					    return new Promise(resolve => setTimeout(resolve, ms))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
class CacheKey {
 | 
					 | 
				
			||||||
    key: string
 | 
					 | 
				
			||||||
    restoreKeys: string[]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructor(key: string, restoreKeys: string[]) {
 | 
					 | 
				
			||||||
        this.key = key
 | 
					 | 
				
			||||||
        this.restoreKeys = restoreKeys
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class CachingReport {
 | 
					 | 
				
			||||||
    cacheEntryReports: CacheEntryReport[] = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    get fullyRestored(): boolean {
 | 
					 | 
				
			||||||
        return this.cacheEntryReports.every(x => !x.wasRequestedButNotRestored())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    entryReport(name: string): CacheEntryReport {
 | 
					 | 
				
			||||||
        for (const report of this.cacheEntryReports) {
 | 
					 | 
				
			||||||
            if (report.entryName === name) {
 | 
					 | 
				
			||||||
                return report
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const newReport = new CacheEntryReport(name)
 | 
					 | 
				
			||||||
        this.cacheEntryReports.push(newReport)
 | 
					 | 
				
			||||||
        return newReport
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    stringify(): string {
 | 
					 | 
				
			||||||
        return JSON.stringify(this)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    static rehydrate(stringRep: string): CachingReport {
 | 
					 | 
				
			||||||
        const rehydrated: CachingReport = Object.assign(new CachingReport(), JSON.parse(stringRep))
 | 
					 | 
				
			||||||
        const entryReports = rehydrated.cacheEntryReports
 | 
					 | 
				
			||||||
        for (let index = 0; index < entryReports.length; index++) {
 | 
					 | 
				
			||||||
            const rawEntryReport = entryReports[index]
 | 
					 | 
				
			||||||
            entryReports[index] = Object.assign(new CacheEntryReport(rawEntryReport.entryName), rawEntryReport)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return rehydrated
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class CacheEntryReport {
 | 
					 | 
				
			||||||
    entryName: string
 | 
					 | 
				
			||||||
    requestedKey: string | undefined
 | 
					 | 
				
			||||||
    requestedRestoreKeys: string[] | undefined
 | 
					 | 
				
			||||||
    restoredKey: string | undefined
 | 
					 | 
				
			||||||
    restoredSize: number | undefined
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    savedKey: string | undefined
 | 
					 | 
				
			||||||
    savedSize: number | undefined
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructor(entryName: string) {
 | 
					 | 
				
			||||||
        this.entryName = entryName
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    wasRequestedButNotRestored(): boolean {
 | 
					 | 
				
			||||||
        return this.requestedKey !== undefined && this.restoredKey === undefined
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    markRequested(key: string, restoreKeys: string[] = []): CacheEntryReport {
 | 
					 | 
				
			||||||
        this.requestedKey = key
 | 
					 | 
				
			||||||
        this.requestedRestoreKeys = restoreKeys
 | 
					 | 
				
			||||||
        return this
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    markRestored(key: string): CacheEntryReport {
 | 
					 | 
				
			||||||
        this.restoredKey = key
 | 
					 | 
				
			||||||
        return this
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    markSaved(key: string): CacheEntryReport {
 | 
					 | 
				
			||||||
        this.savedKey = key
 | 
					 | 
				
			||||||
        return this
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export abstract class AbstractCache {
 | 
					 | 
				
			||||||
    private cacheName: string
 | 
					 | 
				
			||||||
    private cacheDescription: string
 | 
					 | 
				
			||||||
    private cacheKeyStateKey: string
 | 
					 | 
				
			||||||
    private cacheResultStateKey: string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected readonly cacheDebuggingEnabled: boolean
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructor(cacheName: string, cacheDescription: string) {
 | 
					 | 
				
			||||||
        this.cacheName = cacheName
 | 
					 | 
				
			||||||
        this.cacheDescription = cacheDescription
 | 
					 | 
				
			||||||
        this.cacheKeyStateKey = `CACHE_KEY_${cacheName}`
 | 
					 | 
				
			||||||
        this.cacheResultStateKey = `CACHE_RESULT_${cacheName}`
 | 
					 | 
				
			||||||
        this.cacheDebuggingEnabled = isCacheDebuggingEnabled()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    async restore(report: CachingReport): Promise<void> {
 | 
					 | 
				
			||||||
        if (this.cacheOutputExists()) {
 | 
					 | 
				
			||||||
            core.info(`${this.cacheDescription} already exists. Not restoring from cache.`)
 | 
					 | 
				
			||||||
            return
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const cacheKey = this.prepareCacheKey()
 | 
					 | 
				
			||||||
        const entryReport = report.entryReport(this.cacheName)
 | 
					 | 
				
			||||||
        entryReport.markRequested(cacheKey.key, cacheKey.restoreKeys)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.debug(
 | 
					 | 
				
			||||||
            `Requesting ${this.cacheDescription} with
 | 
					 | 
				
			||||||
                key:${cacheKey.key}
 | 
					 | 
				
			||||||
                restoreKeys:[${cacheKey.restoreKeys}]`
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const cacheResult = await this.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)
 | 
					 | 
				
			||||||
        entryReport.markRestored(cacheResult)
 | 
					 | 
				
			||||||
        core.info(`Restored ${this.cacheDescription} from cache key: ${cacheResult}`)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            await this.afterRestore(report)
 | 
					 | 
				
			||||||
        } catch (error) {
 | 
					 | 
				
			||||||
            core.warning(`Restore ${this.cacheDescription} failed in 'afterRestore': ${error}`)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    prepareCacheKey(): CacheKey {
 | 
					 | 
				
			||||||
        const cacheKey = generateCacheKey(this.cacheName)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        core.saveState(this.cacheKeyStateKey, cacheKey.key)
 | 
					 | 
				
			||||||
        return cacheKey
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected async restoreCache(
 | 
					 | 
				
			||||||
        cachePath: string[],
 | 
					 | 
				
			||||||
        cacheKey: string,
 | 
					 | 
				
			||||||
        cacheRestoreKeys: string[] = []
 | 
					 | 
				
			||||||
    ): Promise<string | undefined> {
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            return await cache.restoreCache(cachePath, cacheKey, cacheRestoreKeys)
 | 
					 | 
				
			||||||
        } catch (error) {
 | 
					 | 
				
			||||||
            if (error instanceof cache.ValidationError) {
 | 
					 | 
				
			||||||
                // Validation errors should fail the build action
 | 
					 | 
				
			||||||
                throw error
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            // Warn about any other error and continue
 | 
					 | 
				
			||||||
            core.warning(`Failed to restore ${cacheKey}: ${error}`)
 | 
					 | 
				
			||||||
            return undefined
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected async afterRestore(_report: CachingReport): Promise<void> {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    async save(report: CachingReport): Promise<void> {
 | 
					 | 
				
			||||||
        if (!this.cacheOutputExists()) {
 | 
					 | 
				
			||||||
            this.debug(`No ${this.cacheDescription} to cache.`)
 | 
					 | 
				
			||||||
            return
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const cacheKey = core.getState(this.cacheKeyStateKey)
 | 
					 | 
				
			||||||
        const cacheResult = core.getState(this.cacheResultStateKey)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (!cacheKey) {
 | 
					 | 
				
			||||||
            this.debug(`${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
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            await this.beforeSave(report)
 | 
					 | 
				
			||||||
        } catch (error) {
 | 
					 | 
				
			||||||
            core.warning(`Save ${this.cacheDescription} failed in 'beforeSave': ${error}`)
 | 
					 | 
				
			||||||
            return
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        core.info(`Caching ${this.cacheDescription} with cache key: ${cacheKey}`)
 | 
					 | 
				
			||||||
        const cachePath = this.getCachePath()
 | 
					 | 
				
			||||||
        await this.saveCache(cachePath, cacheKey)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        report.entryReport(this.cacheName).markSaved(cacheKey)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected async beforeSave(_report: CachingReport): Promise<void> {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected async saveCache(cachePath: string[], cacheKey: string): Promise<void> {
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            await cache.saveCache(cachePath, cacheKey)
 | 
					 | 
				
			||||||
        } catch (error) {
 | 
					 | 
				
			||||||
            if (error instanceof cache.ValidationError) {
 | 
					 | 
				
			||||||
                // Validation errors should fail the build action
 | 
					 | 
				
			||||||
                throw error
 | 
					 | 
				
			||||||
            } else if (error instanceof cache.ReserveCacheError) {
 | 
					 | 
				
			||||||
                // Reserve cache errors are expected if the artifact has been previously cached
 | 
					 | 
				
			||||||
                this.debug(error.message)
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                // Warn about any other error and continue
 | 
					 | 
				
			||||||
                core.warning(String(error))
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected debug(message: string): void {
 | 
					 | 
				
			||||||
        if (this.cacheDebuggingEnabled) {
 | 
					 | 
				
			||||||
            core.info(message)
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            core.debug(message)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected abstract cacheOutputExists(): boolean
 | 
					 | 
				
			||||||
    protected abstract getCachePath(): string[]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,8 @@
 | 
				
			|||||||
import {GradleUserHomeCache} from './cache-gradle-user-home'
 | 
					import {GradleUserHomeCache} from './cache-gradle-user-home'
 | 
				
			||||||
import {ProjectDotGradleCache} 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 {CachingReport, isCacheDisabled, isCacheReadOnly} from './cache-utils'
 | 
					import {isCacheDisabled, isCacheReadOnly} from './cache-utils'
 | 
				
			||||||
 | 
					import {CachingReport} from './cache-base'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const BUILD_ROOT_DIR = 'BUILD_ROOT_DIR'
 | 
					const BUILD_ROOT_DIR = 'BUILD_ROOT_DIR'
 | 
				
			||||||
const CACHING_REPORT = 'CACHING_REPORT'
 | 
					const CACHING_REPORT = 'CACHING_REPORT'
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user