diff --git a/action.yml b/action.yml index 5d1469e..c79bfbd 100644 --- a/action.yml +++ b/action.yml @@ -40,6 +40,13 @@ inputs: required: false default: false + cache-encryption-key: + description: | + A base64 encoded AES key used to encrypt the configuration-cache data. The key is exported as 'GRADLE_ENCRYPTION_KEY' for later steps. + A suitable key can be generated with `openssl rand -base64 16`. + Configuration-cache data will not be saved/restored without an encryption key being provided. + required: false + gradle-home-cache-includes: description: Paths within Gradle User Home to cache. required: false diff --git a/src/cache-extract-entries.ts b/src/cache-extract-entries.ts index bd30051..27731ad 100644 --- a/src/cache-extract-entries.ts +++ b/src/cache-extract-entries.ts @@ -1,5 +1,6 @@ import path from 'path' import fs from 'fs' +import crypto from 'crypto' import * as core from '@actions/core' import * as glob from '@actions/glob' @@ -351,14 +352,43 @@ export class ConfigurationCacheEntryExtractor extends AbstractEntryExtractor { * entry is not reusable. */ async restore(listener: CacheListener): Promise { - if (listener.fullyRestored) { - return super.restore(listener) + if (!listener.fullyRestored) { + core.info('Not restoring configuration-cache state, as Gradle User Home was not fully restored') + for (const cacheEntry of this.loadExtractedCacheEntries()) { + listener.entry(cacheEntry.pattern).markNotRestored('Gradle User Home not fully restored') + } + return } - core.info('Not restoring configuration-cache state, as Gradle User Home was not fully restored') - for (const cacheEntry of this.loadExtractedCacheEntries()) { - listener.entry(cacheEntry.pattern).markRequested('NOT_RESTORED') + if (!params.getCacheEncryptionKey()) { + core.info('Not restoring configuration-cache state, as no encryption key was provided') + for (const cacheEntry of this.loadExtractedCacheEntries()) { + listener.entry(cacheEntry.pattern).markNotRestored('No encryption key provided') + } + return } + + const encryptionKey = this.getAESEncryptionKey() + core.exportVariable('GRADLE_ENCRYPTION_KEY', encryptionKey) + return await super.restore(listener) + } + + async extract(listener: CacheListener): Promise { + if (!params.getCacheEncryptionKey()) { + core.info('Not saving configuration-cache state, as no encryption key was provided') + for (const cacheEntry of this.getExtractedCacheEntryDefinitions()) { + listener.entry(cacheEntry.pattern).markNotSaved('No encryption key provided') + } + return + } + + await super.extract(listener) + } + + private getAESEncryptionKey(): string | undefined { + const secret = params.getCacheEncryptionKey() + const key = crypto.pbkdf2Sync(secret, '', 1000, 16, 'sha256') + return key.toString('base64') } /** diff --git a/src/input-params.ts b/src/input-params.ts index 06c2262..d033134 100644 --- a/src/input-params.ts +++ b/src/input-params.ts @@ -29,6 +29,10 @@ export function isCacheCleanupEnabled(): boolean { return getBooleanInput('gradle-home-cache-cleanup') } +export function getCacheEncryptionKey(): string { + return core.getInput('cache-encryption-key') +} + export function getCacheIncludes(): string[] { return core.getMultilineInput('gradle-home-cache-includes') }