2021-08-22 20:14:47 -06:00
import path from 'path'
import fs from 'fs'
import os from 'os'
2021-09-11 09:10:44 -06:00
import * as core from '@actions/core'
import * as glob from '@actions/glob'
2021-09-11 12:08:18 -06:00
import * as exec from '@actions/exec'
2021-08-22 20:14:47 -06:00
2021-09-06 11:16:08 -06:00
import { AbstractCache } from './cache-utils'
2021-08-22 20:14:47 -06:00
2021-09-12 10:08:34 -06:00
// When a common artifact is cached separately, it is replaced by a marker file to allow for restore.
const MARKER_FILE_EXTENSION = '.cached'
// Which paths under Gradle User Home should be cached
// TODO: This should adapt for the `GRADLE_USER_HOME` environment variable
2021-09-13 11:47:59 -06:00
// TODO: Allow the user to override / tweak this set
2021-09-12 10:08:34 -06:00
const CACHE_PATH = [
2021-09-13 11:47:59 -06:00
'~/.gradle/caches' ,
2021-09-12 10:08:34 -06:00
'~/.gradle/notifications' , // Prevent the re-rendering of first-use message for version
2021-09-13 11:47:59 -06:00
` ~/.gradle/wrapper/dists/*/*/*.zip ${ MARKER_FILE_EXTENSION } ` // Only cache/restore wrapper zips: Gradle will automatically expand these on startup if required
]
// Paths to artifacts that are common to all/many Gradle User Home caches
// These artifacts are cached separately to avoid blowing out the size of each GUH cache
// TODO: Allow the user to override / tweak this set
const COMMON_ARTIFACT_PATHS = [
'~/.gradle/caches/*/generated-gradle-jars/*.jar' ,
'~/.gradle/wrapper/dists/*/*/*.zip'
2021-09-12 10:08:34 -06:00
]
2021-09-11 14:50:34 -06:00
2021-09-06 11:16:08 -06:00
export class GradleUserHomeCache extends AbstractCache {
constructor ( ) {
super ( 'gradle' , 'Gradle User Home' )
2021-08-22 20:14:47 -06:00
}
2021-09-15 11:20:33 -06:00
async afterRestore ( ) : Promise < void > {
2021-09-12 13:09:36 -06:00
await this . reportCacheEntrySize ( 'as restored from cache' )
2021-09-12 10:08:34 -06:00
await this . restoreCommonArtifacts ( )
2021-09-12 13:09:36 -06:00
await this . reportCacheEntrySize ( 'after restoring common artifacts' )
2021-09-11 12:08:18 -06:00
}
2021-09-11 09:10:44 -06:00
2021-09-12 10:08:34 -06:00
private async restoreCommonArtifacts ( ) : Promise < void > {
const markerFilePatterns = COMMON_ARTIFACT_PATHS . map ( targetPath = > {
2021-09-11 14:50:34 -06:00
return targetPath + MARKER_FILE_EXTENSION
} ) . join ( '\n' )
2021-09-11 09:10:44 -06:00
2021-09-11 14:50:34 -06:00
const globber = await glob . create ( markerFilePatterns )
const markerFiles = await globber . glob ( )
2021-09-11 12:08:18 -06:00
2021-09-11 20:56:40 -06:00
const processes : Promise < void > [ ] = [ ]
2021-09-11 14:50:34 -06:00
for ( const markerFile of markerFiles ) {
2021-09-12 10:08:34 -06:00
const p = this . restoreCommonArtifact ( markerFile )
2021-09-12 14:08:22 -06:00
// Run sequentially when debugging enabled
if ( this . cacheDebuggingEnabled ) {
await p
}
2021-09-11 20:56:40 -06:00
processes . push ( p )
}
2021-09-12 14:08:22 -06:00
2021-09-11 20:56:40 -06:00
await Promise . all ( processes )
}
2021-09-12 10:08:34 -06:00
private async restoreCommonArtifact ( markerFile : string ) : Promise < void > {
const artifactFile = markerFile . substring (
2021-09-11 20:56:40 -06:00
0 ,
markerFile . length - MARKER_FILE_EXTENSION . length
)
2021-09-12 10:08:34 -06:00
if ( ! fs . existsSync ( artifactFile ) ) {
const key = path . relative ( this . getGradleUserHome ( ) , artifactFile )
const cacheKey = ` gradle-artifact- ${ key } `
2021-09-11 20:56:40 -06:00
2021-09-14 13:30:23 -06:00
const restoreKey = await this . restoreCache ( [ artifactFile ] , cacheKey )
if ( restoreKey ) {
this . debug ( ` Restored ${ cacheKey } from cache to ${ artifactFile } ` )
} else {
this . debug (
` Failed to restore from ${ cacheKey } to ${ artifactFile } `
2021-09-12 10:08:34 -06:00
)
2021-09-11 12:08:18 -06:00
}
2021-09-11 20:56:40 -06:00
} else {
2021-09-12 14:08:22 -06:00
this . debug (
2021-09-12 10:08:34 -06:00
` Artifact file already exists, not restoring: ${ artifactFile } `
)
2021-09-11 12:08:18 -06:00
}
}
2021-09-12 10:08:34 -06:00
private async reportCacheEntrySize ( label : string ) : Promise < void > {
2021-09-12 14:08:22 -06:00
if ( ! this . cacheDebuggingEnabled ) {
return
}
2021-09-11 12:08:18 -06:00
const gradleUserHome = path . resolve ( os . homedir ( ) , '.gradle' )
if ( ! fs . existsSync ( gradleUserHome ) ) {
return
}
2021-09-12 13:09:36 -06:00
const result = await exec . getExecOutput (
'du' ,
[ '-h' , '-c' , '-t' , '5M' ] ,
{
cwd : gradleUserHome ,
silent : true ,
ignoreReturnCode : true
}
)
core . info ( ` Gradle User Home cache entry (directories >5M): ${ label } ` )
core . info (
result . stdout
. trimEnd ( )
. replace ( /\t/g , ' ' )
. split ( '\n' )
. map ( it = > {
return ` ${ it } `
} )
. join ( '\n' )
)
core . info ( '-----------------------' )
2021-09-11 12:08:18 -06:00
}
2021-09-15 11:20:33 -06:00
async beforeSave ( ) : Promise < void > {
2021-09-12 10:08:34 -06:00
await this . saveCommonArtifacts ( )
2021-09-11 12:08:18 -06:00
}
2021-09-12 10:08:34 -06:00
private async saveCommonArtifacts ( ) : Promise < void > {
const artifactFilePatterns = COMMON_ARTIFACT_PATHS . join ( '\n' )
const globber = await glob . create ( artifactFilePatterns )
const commonArtifactFiles = await globber . glob ( )
2021-09-11 09:10:44 -06:00
2021-09-11 20:56:40 -06:00
const processes : Promise < void > [ ] = [ ]
2021-09-12 10:08:34 -06:00
for ( const artifactFile of commonArtifactFiles ) {
const p = this . saveCommonArtifact ( artifactFile )
2021-09-12 14:08:22 -06:00
// Run sequentially when debugging enabled
if ( this . cacheDebuggingEnabled ) {
await p
}
2021-09-11 20:56:40 -06:00
processes . push ( p )
}
2021-09-12 14:08:22 -06:00
2021-09-11 20:56:40 -06:00
await Promise . all ( processes )
}
2021-09-11 09:10:44 -06:00
2021-09-12 10:08:34 -06:00
private async saveCommonArtifact ( artifactFile : string ) : Promise < void > {
const markerFile = ` ${ artifactFile } ${ MARKER_FILE_EXTENSION } `
2021-09-11 20:56:40 -06:00
if ( ! fs . existsSync ( markerFile ) ) {
2021-09-12 10:08:34 -06:00
const filePath = path . relative (
this . getGradleUserHome ( ) ,
artifactFile
)
const cacheKey = ` gradle-artifact- ${ filePath } `
2021-09-12 14:08:22 -06:00
this . debug ( ` Caching ${ artifactFile } with cache key: ${ cacheKey } ` )
2021-09-14 13:30:23 -06:00
await this . saveCache ( [ artifactFile ] , cacheKey )
2021-09-11 12:08:18 -06:00
2021-09-12 10:08:34 -06:00
// Write the marker file that will stand in place of the original
fs . writeFileSync ( markerFile , 'cached' )
2021-09-11 20:56:40 -06:00
} else {
2021-09-12 14:08:22 -06:00
this . debug (
2021-09-12 10:08:34 -06:00
` Marker file already exists: ${ markerFile } . Not caching ${ artifactFile } `
)
2021-09-11 12:08:18 -06:00
}
2021-09-11 20:56:40 -06:00
// TODO : Should not need to delete. Just exclude from cache path.
2021-09-12 10:08:34 -06:00
// Delete the original artifact file
fs . unlinkSync ( artifactFile )
}
protected getGradleUserHome ( ) : string {
return path . resolve ( os . homedir ( ) , '.gradle' )
2021-09-11 09:10:44 -06:00
}
2021-09-06 11:16:08 -06:00
protected cacheOutputExists ( ) : boolean {
// Need to check for 'caches' directory to avoid incorrect detection on MacOS agents
2021-09-11 14:50:34 -06:00
const dir = path . resolve ( this . getGradleUserHome ( ) , 'caches' )
2021-09-06 11:16:08 -06:00
return fs . existsSync ( dir )
2021-08-22 20:14:47 -06:00
}
2021-09-06 11:16:08 -06:00
protected getCachePath ( ) : string [ ] {
return CACHE_PATH
2021-08-22 20:14:47 -06:00
}
}