2022-06-20 20:17:36 -06:00
import * as cache from '@actions/cache'
2021-12-07 12:29:37 -07:00
2024-07-17 18:38:02 -06:00
export const DEFAULT_CACHE_ENABLED_REASON = ` [Cache was enabled](https://github.com/gradle/actions/blob/v3/docs/setup-gradle.md#caching-build-state-between-jobs). Action attempted to both restore and save the Gradle User Home. `
2024-07-17 14:36:18 -06:00
2024-07-17 18:38:02 -06:00
export const DEFAULT_READONLY_REASON = ` [Cache was read-only](https://github.com/gradle/actions/blob/v3/docs/setup-gradle.md#using-the-cache-read-only). By default, the action will only write to the cache for Jobs running on the default branch. `
2024-07-17 14:36:18 -06:00
2024-07-17 18:38:02 -06:00
export const DEFAULT_DISABLED_REASON = ` [Cache was disabled](https://github.com/gradle/actions/blob/v3/docs/setup-gradle.md#disabling-caching) via action confiugration. Gradle User Home was not restored from or saved to the cache. `
2024-07-17 14:36:18 -06:00
2024-07-17 18:38:02 -06:00
export const DEFAULT_WRITEONLY_REASON = ` [Cache was set to write-only](https://github.com/gradle/actions/blob/v3/docs/setup-gradle.md#using-the-cache-write-only) via action configuration. Gradle User Home was not restored from cache. `
export const EXISTING_GRADLE_HOME = ` [Cache was disabled to avoid overwriting a pre-existing Gradle User Home](https://github.com/gradle/actions/blob/v3/docs/setup-gradle.md#overwriting-an-existing-gradle-user-home). Gradle User Home was not restored from or saved to the cache. `
2024-07-17 19:02:31 -06:00
export const CLEANUP_DISABLED_READONLY = ` [Cache cleanup](https://github.com/gradle/actions/blob/v3/docs/setup-gradle.md#enabling-cache-cleanup) is always disabled when cache is read-only or disabled. `
2024-07-17 18:38:02 -06:00
2024-07-17 19:02:31 -06:00
export const DEFAULT_CLEANUP_DISABLED_REASON = ` [Cache cleanup](https://github.com/gradle/actions/blob/v3/docs/setup-gradle.md#enabling-cache-cleanup) was not enabled. It must be explicitly enabled. `
export const DEFAULT_CLEANUP_ENABLED_REASON = ` [Cache cleanup](https://github.com/gradle/actions/blob/v3/docs/setup-gradle.md#enabling-cache-cleanup) was enabled. `
export const CLEANUP_DISABLED_DUE_TO_FAILURE =
'[Cache cleanup was disabled due to build failure](https://github.com/gradle/actions/blob/v3/docs/setup-gradle.md#enabling-cache-cleanup). Use `cache-cleanup: always` to override this behavior.'
2024-07-17 14:36:18 -06:00
2021-12-07 12:29:37 -07:00
/ * *
* Collects information on what entries were saved and restored during the action .
* This information is used to generate a summary of the cache usage .
* /
export class CacheListener {
cacheEntries : CacheEntryListener [ ] = [ ]
2022-06-22 16:35:55 -06:00
cacheReadOnly = false
cacheWriteOnly = false
cacheDisabled = false
2024-07-17 18:38:02 -06:00
cacheStatusReason : string = DEFAULT_CACHE_ENABLED_REASON
cacheCleanupMessage : string = DEFAULT_CLEANUP_DISABLED_REASON
2021-12-07 12:29:37 -07:00
get fullyRestored ( ) : boolean {
return this . cacheEntries . every ( x = > ! x . wasRequestedButNotRestored ( ) )
}
2022-06-04 09:36:41 -06:00
get cacheStatus ( ) : string {
2022-06-20 20:17:36 -06:00
if ( ! cache . isFeatureAvailable ( ) ) return 'not available'
2024-07-17 14:36:18 -06:00
if ( this . cacheDisabled ) return 'disabled'
2022-06-22 16:35:55 -06:00
if ( this . cacheWriteOnly ) return 'write-only'
if ( this . cacheReadOnly ) return 'read-only'
2022-06-04 09:36:41 -06:00
return 'enabled'
}
2024-07-17 14:36:18 -06:00
setReadOnly ( reason : string = DEFAULT_READONLY_REASON ) : void {
this . cacheReadOnly = true
this . cacheStatusReason = reason
2024-07-17 19:02:31 -06:00
this . cacheCleanupMessage = CLEANUP_DISABLED_READONLY
2024-07-17 14:36:18 -06:00
}
setDisabled ( reason : string = DEFAULT_DISABLED_REASON ) : void {
this . cacheDisabled = true
this . cacheStatusReason = reason
2024-07-17 19:02:31 -06:00
this . cacheCleanupMessage = CLEANUP_DISABLED_READONLY
2024-07-17 14:36:18 -06:00
}
setWriteOnly ( reason : string = DEFAULT_WRITEONLY_REASON ) : void {
this . cacheWriteOnly = true
this . cacheStatusReason = reason
}
2024-07-17 18:38:02 -06:00
setCacheCleanupEnabled ( ) : void {
this . cacheCleanupMessage = DEFAULT_CLEANUP_ENABLED_REASON
}
2024-07-17 19:02:31 -06:00
setCacheCleanupDisabled ( reason : string = DEFAULT_CLEANUP_DISABLED_REASON ) : void {
this . cacheCleanupMessage = reason
}
2021-12-07 12:29:37 -07:00
entry ( name : string ) : CacheEntryListener {
for ( const entry of this . cacheEntries ) {
if ( entry . entryName === name ) {
return entry
}
}
const newEntry = new CacheEntryListener ( name )
this . cacheEntries . push ( newEntry )
return newEntry
}
stringify ( ) : string {
return JSON . stringify ( this )
}
static rehydrate ( stringRep : string ) : CacheListener {
2022-01-20 09:36:57 -07:00
if ( stringRep === '' ) {
return new CacheListener ( )
}
2021-12-07 12:29:37 -07:00
const rehydrated : CacheListener = Object . assign ( new CacheListener ( ) , JSON . parse ( stringRep ) )
const entries = rehydrated . cacheEntries
for ( let index = 0 ; index < entries . length ; index ++ ) {
const rawEntry = entries [ index ]
entries [ index ] = Object . assign ( new CacheEntryListener ( rawEntry . entryName ) , rawEntry )
}
return rehydrated
}
}
/ * *
* Collects information on the state of a single cache entry .
* /
export class CacheEntryListener {
entryName : string
requestedKey : string | undefined
requestedRestoreKeys : string [ ] | undefined
restoredKey : string | undefined
restoredSize : number | undefined
2022-08-16 15:29:23 -06:00
notRestored : string | undefined
2021-12-07 12:29:37 -07:00
savedKey : string | undefined
savedSize : number | undefined
2022-08-16 15:29:23 -06:00
notSaved : string | undefined
2022-06-02 22:39:55 -06:00
2021-12-07 12:29:37 -07:00
constructor ( entryName : string ) {
this . entryName = entryName
}
wasRequestedButNotRestored ( ) : boolean {
return this . requestedKey !== undefined && this . restoredKey === undefined
}
markRequested ( key : string , restoreKeys : string [ ] = [ ] ) : CacheEntryListener {
this . requestedKey = key
this . requestedRestoreKeys = restoreKeys
return this
}
markRestored ( key : string , size : number | undefined ) : CacheEntryListener {
this . restoredKey = key
this . restoredSize = size
return this
}
2022-08-16 15:29:23 -06:00
markNotRestored ( message : string ) : CacheEntryListener {
this . notRestored = message
return this
}
2021-12-07 12:29:37 -07:00
markSaved ( key : string , size : number | undefined ) : CacheEntryListener {
this . savedKey = key
this . savedSize = size
return this
}
2022-01-19 12:11:51 -07:00
markAlreadyExists ( key : string ) : CacheEntryListener {
this . savedKey = key
this . savedSize = 0
return this
}
2022-06-02 22:39:55 -06:00
2022-08-16 15:29:23 -06:00
markNotSaved ( message : string ) : CacheEntryListener {
this . notSaved = message
2022-06-02 22:39:55 -06:00
return this
}
2021-12-07 12:29:37 -07:00
}
2023-12-31 14:42:48 -07:00
export function generateCachingReport ( listener : CacheListener ) : string {
2022-06-04 09:36:41 -06:00
const entries = listener . cacheEntries
2021-12-07 12:29:37 -07:00
2023-12-31 14:42:48 -07:00
return `
< details >
2024-01-25 12:28:05 -07:00
< summary > < h4 > Caching for Gradle actions was $ { listener . cacheStatus } - expand for details < / h4 > < / summary >
2024-07-17 14:36:18 -06:00
2024-07-17 18:38:02 -06:00
- $ { listener . cacheStatusReason }
- $ { listener . cacheCleanupMessage }
2024-07-17 14:36:18 -06:00
2023-12-31 14:42:48 -07:00
$ { renderEntryTable ( entries ) }
< h5 > Cache Entry Details < / h5 >
< pre >
$ { renderEntryDetails ( listener ) }
2022-06-20 17:53:30 -06:00
< / pre >
< / details >
2023-12-31 14:42:48 -07:00
`
2022-06-20 17:53:30 -06:00
}
2023-12-31 14:42:48 -07:00
function renderEntryTable ( entries : CacheEntryListener [ ] ) : string {
return `
< table >
< tr > < td > < / td > < th > Count < / th > < th > Total Size ( Mb ) < / th > < / tr >
< tr > < td > Entries Restored < / td >
< td > $ { getCount ( entries , e = > e . restoredSize ) } < / td >
< td > $ { getSize ( entries , e = > e . restoredSize ) } < / td >
< / tr >
< tr > < td > Entries Saved < / td >
< td > $ { getCount ( entries , e = > e . savedSize ) } < / td >
< td > $ { getSize ( entries , e = > e . savedSize ) } < / td >
< / tr >
< / table >
`
2022-06-20 17:53:30 -06:00
}
function renderEntryDetails ( listener : CacheListener ) : string {
return listener . cacheEntries
2022-06-02 14:21:10 -06:00
. map (
2022-06-20 17:53:30 -06:00
entry = > ` Entry: ${ entry . entryName }
2021-12-07 12:29:37 -07:00
Requested Key : $ { entry . requestedKey ? ? '' }
Restored Key : $ { entry . restoredKey ? ? '' }
Size : $ { formatSize ( entry . restoredSize ) }
2022-06-22 16:35:55 -06:00
$ { getRestoredMessage ( entry , listener . cacheWriteOnly ) }
2021-12-07 12:29:37 -07:00
Saved Key : $ { entry . savedKey ? ? '' }
2022-06-02 22:39:55 -06:00
Size : $ { formatSize ( entry . savedSize ) }
2022-06-22 16:35:55 -06:00
$ { getSavedMessage ( entry , listener . cacheReadOnly ) }
2022-06-02 14:21:10 -06:00
`
2022-06-04 09:36:41 -06:00
)
. join ( '---\n' )
2021-12-07 12:29:37 -07:00
}
2022-06-22 16:35:55 -06:00
function getRestoredMessage ( entry : CacheEntryListener , cacheWriteOnly : boolean ) : string {
2022-08-16 15:29:23 -06:00
if ( entry . notRestored ) {
return ` (Entry not restored: ${ entry . notRestored } ) `
}
2022-06-22 16:35:55 -06:00
if ( cacheWriteOnly ) {
2022-06-02 22:44:08 -06:00
return '(Entry not restored: cache is write-only)'
}
2022-08-16 15:29:23 -06:00
if ( entry . requestedKey === undefined ) {
return '(Entry not restored: not requested)'
}
2022-06-02 22:39:55 -06:00
if ( entry . restoredKey === undefined ) {
2022-06-02 22:44:08 -06:00
return '(Entry not restored: no match found)'
2022-06-02 22:39:55 -06:00
}
if ( entry . restoredKey === entry . requestedKey ) {
2022-06-02 22:44:08 -06:00
return '(Entry restored: exact match found)'
2022-06-02 22:39:55 -06:00
}
2022-06-02 22:44:08 -06:00
return '(Entry restored: partial match found)'
2022-06-02 22:39:55 -06:00
}
2022-06-22 16:35:55 -06:00
function getSavedMessage ( entry : CacheEntryListener , cacheReadOnly : boolean ) : string {
2022-08-16 15:29:23 -06:00
if ( entry . notSaved ) {
return ` (Entry not saved: ${ entry . notSaved } ) `
2022-06-02 22:39:55 -06:00
}
if ( entry . savedKey === undefined ) {
2022-06-22 16:35:55 -06:00
if ( cacheReadOnly ) {
2022-06-02 22:44:08 -06:00
return '(Entry not saved: cache is read-only)'
}
2023-12-23 11:26:48 -07:00
if ( entry . notRestored ) {
return '(Entry not saved: not restored)'
}
2022-06-02 22:44:08 -06:00
return '(Entry not saved: reason unknown)'
2022-06-02 22:39:55 -06:00
}
if ( entry . savedSize === 0 ) {
2022-06-02 22:44:08 -06:00
return '(Entry not saved: entry with key already exists)'
2022-06-02 22:39:55 -06:00
}
return '(Entry saved)'
}
2021-12-07 12:29:37 -07:00
function getCount (
cacheEntries : CacheEntryListener [ ] ,
predicate : ( value : CacheEntryListener ) = > number | undefined
) : number {
2022-08-16 15:29:23 -06:00
return cacheEntries . filter ( e = > predicate ( e ) ) . length
2021-12-07 12:29:37 -07:00
}
2022-06-04 09:36:41 -06:00
function getSize (
2022-06-02 14:21:10 -06:00
cacheEntries : CacheEntryListener [ ] ,
predicate : ( value : CacheEntryListener ) = > number | undefined
) : number {
2022-06-04 09:36:41 -06:00
const bytes = cacheEntries . map ( e = > predicate ( e ) ? ? 0 ) . reduce ( ( p , v ) = > p + v , 0 )
2022-06-02 14:21:10 -06:00
return Math . round ( bytes / ( 1024 * 1024 ) )
2021-12-07 12:29:37 -07:00
}
function formatSize ( bytes : number | undefined ) : string {
2022-06-06 14:57:03 -06:00
if ( bytes === undefined || bytes === 0 ) {
2021-12-07 12:29:37 -07:00
return ''
}
return ` ${ Math . round ( bytes / ( 1024 * 1024 ) ) } MB ( ${ bytes } B) `
}