Extract cache-reporting into separate file

Cache reporting is self-contained enough that it deserves some separation.
This commit is contained in:
Daz DeBoer 2021-12-07 12:29:37 -07:00
parent 69aad1f173
commit 253d6427fd
No known key found for this signature in database
GPG key ID: DD6B9F0B06683D5D
5 changed files with 129 additions and 126 deletions

View file

@ -1,4 +1,4 @@
import {CacheEntryListener, CacheListener} from '../src/cache-base' import {CacheEntryListener, CacheListener} from '../src/cache-reporting'
describe('caching report', () => { describe('caching report', () => {
describe('reports not fully restored', () => { describe('reports not fully restored', () => {

View file

@ -1,6 +1,7 @@
import * as core from '@actions/core' import * as core from '@actions/core'
import * as cache from '@actions/cache' import * as cache from '@actions/cache'
import * as github from '@actions/github' import * as github from '@actions/github'
import {CacheListener} from './cache-reporting'
import {isCacheDebuggingEnabled, getCacheKeyPrefix, hashStrings, handleCacheFailure} from './cache-utils' import {isCacheDebuggingEnabled, getCacheKeyPrefix, hashStrings, handleCacheFailure} from './cache-utils'
const CACHE_PROTOCOL_VERSION = 'v5-' const CACHE_PROTOCOL_VERSION = 'v5-'
@ -64,84 +65,6 @@ function determineJobContext(): string {
return hashStrings([workflowJobContext]) return hashStrings([workflowJobContext])
} }
/**
* 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[] = []
get fullyRestored(): boolean {
return this.cacheEntries.every(x => !x.wasRequestedButNotRestored())
}
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 {
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
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[] = []): CacheEntryListener {
this.requestedKey = key
this.requestedRestoreKeys = restoreKeys
return this
}
markRestored(key: string, size: number | undefined): CacheEntryListener {
this.restoredKey = key
this.restoredSize = size
return this
}
markSaved(key: string, size: number | undefined): CacheEntryListener {
this.savedKey = key
this.savedSize = size
return this
}
}
export abstract class AbstractCache { export abstract class AbstractCache {
private cacheName: string private cacheName: string
private cacheDescription: string private cacheDescription: string

View file

@ -5,7 +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 {AbstractCache, CacheEntryListener, CacheListener} from './cache-base' import {AbstractCache} from './cache-base'
import {CacheEntryListener, CacheListener} from './cache-reporting'
import {getCacheKeyPrefix, hashFileNames, tryDelete} from './cache-utils' import {getCacheKeyPrefix, hashFileNames, tryDelete} from './cache-utils'
const META_FILE_DIR = '.gradle-build-action' const META_FILE_DIR = '.gradle-build-action'

123
src/cache-reporting.ts Normal file
View file

@ -0,0 +1,123 @@
import * as core from '@actions/core'
/**
* 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[] = []
get fullyRestored(): boolean {
return this.cacheEntries.every(x => !x.wasRequestedButNotRestored())
}
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 {
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
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[] = []): CacheEntryListener {
this.requestedKey = key
this.requestedRestoreKeys = restoreKeys
return this
}
markRestored(key: string, size: number | undefined): CacheEntryListener {
this.restoredKey = key
this.restoredSize = size
return this
}
markSaved(key: string, size: number | undefined): CacheEntryListener {
this.savedKey = key
this.savedSize = size
return this
}
}
export function logCachingReport(listener: CacheListener): void {
if (listener.cacheEntries.length === 0) {
return
}
core.info(`---------- Caching Summary -------------
Restored Entries Count: ${getCount(listener.cacheEntries, e => e.restoredSize)}
Size: ${getSum(listener.cacheEntries, e => e.restoredSize)}
Saved Entries Count: ${getCount(listener.cacheEntries, e => e.savedSize)}
Size: ${getSum(listener.cacheEntries, e => e.savedSize)}`)
core.startGroup('Cache Entry details')
for (const entry of listener.cacheEntries) {
core.info(`Entry: ${entry.entryName}
Requested Key : ${entry.requestedKey ?? ''}
Restored Key : ${entry.restoredKey ?? ''}
Size: ${formatSize(entry.restoredSize)}
Saved Key : ${entry.savedKey ?? ''}
Size: ${formatSize(entry.savedSize)}`)
}
core.endGroup()
}
function getCount(
cacheEntries: CacheEntryListener[],
predicate: (value: CacheEntryListener) => number | undefined
): number {
return cacheEntries.filter(e => predicate(e) !== undefined).length
}
function getSum(
cacheEntries: CacheEntryListener[],
predicate: (value: CacheEntryListener) => number | undefined
): string {
return formatSize(cacheEntries.map(e => predicate(e) ?? 0).reduce((p, v) => p + v, 0))
}
function formatSize(bytes: number | undefined): string {
if (bytes === undefined || bytes === 0) {
return ''
}
return `${Math.round(bytes / (1024 * 1024))} MB (${bytes} B)`
}

View file

@ -1,8 +1,8 @@
import * as core from '@actions/core'
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 {isCacheDisabled, isCacheReadOnly} from './cache-utils' import {isCacheDisabled, isCacheReadOnly} from './cache-utils'
import {CacheEntryListener, CacheListener} from './cache-base' import {logCachingReport, CacheListener} from './cache-reporting'
const BUILD_ROOT_DIR = 'BUILD_ROOT_DIR' const BUILD_ROOT_DIR = 'BUILD_ROOT_DIR'
const CACHE_LISTENER = 'CACHE_LISTENER' const CACHE_LISTENER = 'CACHE_LISTENER'
@ -56,47 +56,3 @@ export async function save(): Promise<void> {
logCachingReport(cacheListener) logCachingReport(cacheListener)
} }
function logCachingReport(listener: CacheListener): void {
if (listener.cacheEntries.length === 0) {
return
}
core.info(`---------- Caching Summary -------------
Restored Entries Count: ${getCount(listener.cacheEntries, e => e.restoredSize)}
Size: ${getSum(listener.cacheEntries, e => e.restoredSize)}
Saved Entries Count: ${getCount(listener.cacheEntries, e => e.savedSize)}
Size: ${getSum(listener.cacheEntries, e => e.savedSize)}`)
core.startGroup('Cache Entry details')
for (const entry of listener.cacheEntries) {
core.info(`Entry: ${entry.entryName}
Requested Key : ${entry.requestedKey ?? ''}
Restored Key : ${entry.restoredKey ?? ''}
Size: ${formatSize(entry.restoredSize)}
Saved Key : ${entry.savedKey ?? ''}
Size: ${formatSize(entry.savedSize)}`)
}
core.endGroup()
}
function getCount(
cacheEntries: CacheEntryListener[],
predicate: (value: CacheEntryListener) => number | undefined
): number {
return cacheEntries.filter(e => predicate(e) !== undefined).length
}
function getSum(
cacheEntries: CacheEntryListener[],
predicate: (value: CacheEntryListener) => number | undefined
): string {
return formatSize(cacheEntries.map(e => predicate(e) ?? 0).reduce((p, v) => p + v, 0))
}
function formatSize(bytes: number | undefined): string {
if (bytes === undefined || bytes === 0) {
return ''
}
return `${Math.round(bytes / (1024 * 1024))} MB (${bytes} B)`
}