diff --git a/.github/workflows/prod.yml b/.github/workflows/prod.yml index 114e10f..eaccaef 100644 --- a/.github/workflows/prod.yml +++ b/.github/workflows/prod.yml @@ -51,12 +51,6 @@ jobs: gradle-executable: __tests__/samples/basic/gradlew${{ matrix.script-suffix }} build-root-directory: __tests__/samples/no-wrapper arguments: help - - name: Test custom wrapper location (deprecated) - uses: ./ - with: - build-root-directory: __tests__/samples/no-wrapper - wrapper-directory: __tests__/samples/basic - arguments: help dependencies-cache: needs: basic-build diff --git a/__tests__/cache-utils.test.ts b/__tests__/cache-utils.test.ts index da0e35a..89a7715 100644 --- a/__tests__/cache-utils.test.ts +++ b/__tests__/cache-utils.test.ts @@ -2,40 +2,6 @@ import * as cacheUtils from '../src/cache-utils' import * as path from 'path' describe('cacheUtils-utils', () => { - describe('can hash', () => { - it('a directory', async () => { - const hash = await cacheUtils.hashFiles( - path.resolve('__tests__/data/crypto-utils-test/gradle') - ) - expect(hash).toBe( - process.platform === 'win32' - ? '3364336e94e746ce65a31748a6371b7efd7d499e18ad605c74c91cde0edc0a44' - : '63b9f14f65d014e585099c9c274b9dcbddf5cfd1a8978e5a24efb89ff9304348' - ) - }) - it('a directory with a glob', async () => { - const hash = await cacheUtils.hashFiles( - path.resolve('__tests__/data/crypto-utils-test/'), - ['gradle/**'] - ) - expect(hash).toBe( - process.platform === 'win32' - ? '3364336e94e746ce65a31748a6371b7efd7d499e18ad605c74c91cde0edc0a44' - : '63b9f14f65d014e585099c9c274b9dcbddf5cfd1a8978e5a24efb89ff9304348' - ) - }) - it('a directory with globs', async () => { - const hash = await cacheUtils.hashFiles( - path.resolve('__tests__/data/crypto-utils-test/'), - ['**/*.gradle', 'gradle/**'] - ) - expect(hash).toBe( - process.platform === 'win32' - ? 'd9b66fded38f79f601ce745d64ed726a8df8c0b242b02bcd2c1d331f54742ad6' - : 'f42cd10636f09799f4e01cc84e7ae906cc1d9140f1446f8dcd054d19cbc44c2b' - ) - }) - }) describe('can truncate args', () => { test('handles zero-length string', () => { expect(cacheUtils.truncateArgs('')).toBe('') diff --git a/__tests__/cache-wrapper.test.ts b/__tests__/cache-wrapper.test.ts deleted file mode 100644 index d4d6a45..0000000 --- a/__tests__/cache-wrapper.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import * as cacheWrapper from '../src/cache-wrapper' -import * as path from 'path' - -describe('cache', () => { - describe('can extract gradle wrapper slug', () => { - it('from wrapper properties file', async () => { - const version = cacheWrapper.extractGradleWrapperSlugFrom( - path.resolve( - '__tests__/data/cache-wrapper-test/gradle-wrapper.properties' - ) - ) - expect(version).toBe('6.6.1-bin') - }) - it('for -bin dist', async () => { - const version = cacheWrapper.extractGradleWrapperSlugFromDistUri( - 'distributionUrl=https\\://services.gradle.org/distributions/gradle-6.6.1-bin.zip' - ) - expect(version).toBe('6.6.1-bin') - }) - it('for -all dist', async () => { - const version = cacheWrapper.extractGradleWrapperSlugFromDistUri( - 'distributionUrl=https\\://services.gradle.org/distributions/gradle-6.6.1-all.zip' - ) - expect(version).toBe('6.6.1-all') - }) - it('for milestone', async () => { - const version = cacheWrapper.extractGradleWrapperSlugFromDistUri( - 'distributionUrl=https\\://services.gradle.org/distributions/gradle-6.6-milestone-1-all.zip' - ) - expect(version).toBe('6.6-milestone-1-all') - }) - }) -}) diff --git a/action.yml b/action.yml index 5f5511f..40758cc 100644 --- a/action.yml +++ b/action.yml @@ -4,10 +4,6 @@ description: 'Executes a Gradle build, caching useful state in the GitHub action # https://help.github.com/en/articles/metadata-syntax-for-github-actions inputs: - wrapper-directory: - description: Path to the Gradle Wrapper directory - required: false - deprecationMessage: Use 'gradle-executable' to point to a gradlew[.bat] file in a non-default location gradle-executable: description: Path to the Gradle executable required: false @@ -32,37 +28,6 @@ inputs: description: Whether caching of state in project .gradle dir is enabled, default to 'false' required: false default: true - wrapper-cache-enabled: - description: Whether caching wrapper installation is enabled or not, default to 'true' - required: false - default: true - deprecationMessage: Replaced by 'distributions-cache-enabled' which enables caching for all downloaded Gradle distributions - dependencies-cache-enabled: - description: Whether caching dependencies is enabled or not, default to 'false' - required: false - default: false - dependencies-cache-key: - description: Globs of files to hash in the build root directory, separated by new lines, use best-effort if unset - required: false - dependencies-cache-exact: - description: Whether to restore only if exact match, default to 'false' - required: false - default: false - configuration-cache-enabled: - description: Whether caching build configuration is enabled or not, default to 'false' - required: false - default: false - configuration-cache-key: - description: Globs of files to hash in the build root directory, separated by new lines, use best-effort if unset - required: false - configuration-cache-exact: - description: Whether to restore only if exact match, default to 'false' - required: false - default: false - cache-read-only: - description: When 'true', existing entries will be read from the cache but no entries will be written - required: false - default: false outputs: build-scan-url: diff --git a/src/cache-configuration.ts b/src/cache-configuration.ts deleted file mode 100644 index 432c82b..0000000 --- a/src/cache-configuration.ts +++ /dev/null @@ -1,113 +0,0 @@ -import * as path from 'path' -import * as fs from 'fs' - -import * as core from '@actions/core' -import * as cache from '@actions/cache' - -import * as cacheUtils from './cache-utils' - -import { - inputCacheKeyGlobs, - tryDeleteFiles, - isDependenciesCacheDisabled -} from './cache-dependencies' - -const CONFIGURATION_CACHE_PATH = 'CONFIGURATION_CACHE_PATH' -const CONFIGURATION_CACHE_KEY = 'CONFIGURATION_CACHE_KEY' -const CONFIGURATION_CACHE_RESULT = 'CONFIGURATION_CACHE_RESULT' - -export async function restoreCachedConfiguration( - rootDir: string -): Promise { - if (isConfigurationCacheDisabled()) return - - if (isDependenciesCacheDisabled()) { - throw new Error( - `Must enable dependencies-cache when configuration-cache is enabled` - ) - } - - const cachePath = path.resolve(rootDir, '.gradle/configuration-cache') - if (fs.existsSync(cachePath)) return - core.saveState(CONFIGURATION_CACHE_PATH, cachePath) - - const inputCacheExact = core.getBooleanInput('configuration-cache-exact') - const cacheKeyPrefix = 'configuration|' - - const args = core.getInput('arguments') - const argsKey = cacheUtils.truncateArgs(args) - const cacheKeyWithArgs = `${cacheKeyPrefix}${argsKey}|` - - const cacheKeyGlobs = inputCacheKeyGlobs('configuration-cache-key') - const hash = await cacheUtils.hashFiles(rootDir, cacheKeyGlobs) - const cacheKey = `${cacheKeyWithArgs}${hash}` - - core.saveState(CONFIGURATION_CACHE_KEY, cacheKey) - - const cacheResult = await cache.restoreCache( - [cachePath], - cacheKey, - inputCacheExact ? [] : [cacheKeyWithArgs, cacheKeyPrefix] - ) - - if (!cacheResult) { - core.info( - 'Configuration cache not found, expect task graph calculation.' - ) - return - } - - core.saveState(CONFIGURATION_CACHE_RESULT, cacheResult) - core.info(`Configuration restored from cache key: ${cacheResult}`) - return -} - -export async function cacheConfiguration(): Promise { - if (isConfigurationCacheDisabled()) return - - const cachePath = core.getState(CONFIGURATION_CACHE_PATH) - const cacheKey = core.getState(CONFIGURATION_CACHE_KEY) - const cacheResult = core.getState(CONFIGURATION_CACHE_RESULT) - - if (!cachePath || !fs.existsSync(cachePath)) { - core.debug('No configuration to cache.') - return - } - - if (cacheResult && cacheKey === cacheResult) { - core.info( - `Configuration cache hit occurred on the cache key ${cacheKey}, not saving cache.` - ) - return - } - - const locksDeleted = tryDeleteFiles([ - path.resolve(cachePath, 'configuration-cache.lock') - ]) - if (!locksDeleted) { - core.warning( - 'Unable to delete configuration lock files, try using --no-daemon or stopping the daemon last if you have several Gradle steps, not saving cache.' - ) - return - } - - core.info(`Will cache configuration with key ${cacheKey}`) - - try { - await cache.saveCache([cachePath], cacheKey) - } catch (error) { - if (error.name === cache.ValidationError.name) { - throw error - } else if (error.name === cache.ReserveCacheError.name) { - core.info(error.message) - } else { - core.info(`[warning] ${error.message}`) - } - } - - return -} - -function isConfigurationCacheDisabled(): boolean { - return !core.getBooleanInput('configuration-cache-enabled') -} diff --git a/src/cache-dependencies.ts b/src/cache-dependencies.ts deleted file mode 100644 index 8bae134..0000000 --- a/src/cache-dependencies.ts +++ /dev/null @@ -1,126 +0,0 @@ -import * as path from 'path' -import * as fs from 'fs' -import * as os from 'os' - -import * as core from '@actions/core' -import * as cache from '@actions/cache' - -import * as cacheUtils from './cache-utils' - -const DEPENDENCIES_CACHE_PATH = 'DEPENDENCIES_CACHE_PATH' -const DEPENDENCIES_CACHE_KEY = 'DEPENDENCIES_CACHE_KEY' -const DEPENDENCIES_CACHE_RESULT = 'DEPENDENCIES_CACHE_RESULT' - -export async function restoreCachedDependencies( - rootDir: string -): Promise { - if (isDependenciesCacheDisabled()) return - - const cachePath = path.resolve(os.homedir(), '.gradle/caches/modules-2') - if (fs.existsSync(cachePath)) return - core.saveState(DEPENDENCIES_CACHE_PATH, cachePath) - - const inputCacheExact = core.getBooleanInput('dependencies-cache-exact') - const cacheKeyPrefix = 'dependencies|' - - const args = core.getInput('arguments') - const argsKey = cacheUtils.truncateArgs(args) - const cacheKeyWithArgs = `${cacheKeyPrefix}${argsKey}|` - - const cacheKeyGlobs = inputCacheKeyGlobs('dependencies-cache-key') - const hash = await cacheUtils.hashFiles(rootDir, cacheKeyGlobs) - const cacheKey = `${cacheKeyWithArgs}${hash}` - - core.saveState(DEPENDENCIES_CACHE_KEY, cacheKey) - - const cacheResult = await cache.restoreCache( - [cachePath], - cacheKey, - inputCacheExact ? [] : [cacheKeyWithArgs, cacheKeyPrefix] - ) - - if (!cacheResult) { - core.info('Dependencies cache not found, expect dependencies download.') - return - } - - core.saveState(DEPENDENCIES_CACHE_RESULT, cacheResult) - core.info(`Dependencies restored from cache key: ${cacheResult}`) - return -} - -export async function cacheDependencies(): Promise { - if (isDependenciesCacheDisabled()) return - - const cachePath = core.getState(DEPENDENCIES_CACHE_PATH) - const cacheKey = core.getState(DEPENDENCIES_CACHE_KEY) - const cacheResult = core.getState(DEPENDENCIES_CACHE_RESULT) - - if (!cachePath || !fs.existsSync(cachePath)) { - core.debug('No dependencies to cache.') - return - } - - if (cacheResult && cacheKey === cacheResult) { - core.info( - `Dependencies cache hit occurred on the cache key ${cacheKey}, not saving cache.` - ) - return - } - - const locksDeleted = tryDeleteFiles([ - path.resolve(cachePath, 'modules-2.lock') - ]) - if (!locksDeleted) { - core.warning( - 'Unable to delete dependencies lock files, try using --no-daemon or stopping the daemon last if you have several Gradle steps, not saving cache.' - ) - return - } - - core.info(`Will cache dependencies with key ${cacheKey}`) - - try { - await cache.saveCache([cachePath], cacheKey) - } catch (error) { - if (error.name === cache.ValidationError.name) { - throw error - } else if (error.name === cache.ReserveCacheError.name) { - core.info(error.message) - } else { - core.info(`[warning] ${error.message}`) - } - } - - return -} - -export function tryDeleteFiles(filePaths: string[]): boolean { - let failure = false - for (const filePath of filePaths) { - if (fs.existsSync(filePath)) { - try { - fs.unlinkSync(filePath) - } catch (error) { - failure = true - } - } - } - return !failure -} - -export function isDependenciesCacheDisabled(): boolean { - return !core.getBooleanInput('dependencies-cache-enabled') -} - -export function inputCacheKeyGlobs(input: string): string[] { - const inputValue = core.getMultilineInput(input) - return inputValue.length > 0 - ? inputValue - : [ - '**/*.gradle', - '**/*.gradle.kts', - '**/gradle.properties', - 'gradle/**' - ] -} diff --git a/src/cache-utils.ts b/src/cache-utils.ts index 1a7195f..17a61ff 100644 --- a/src/cache-utils.ts +++ b/src/cache-utils.ts @@ -1,17 +1,3 @@ -import * as path from 'path' -import * as glob from '@actions/glob' - -export async function hashFiles( - baseDir: string, - patterns: string[] = ['**'], - followSymbolicLinks = false -): Promise { - const combinedPatterns = patterns - .map(pattern => `${baseDir}${path.sep}${pattern}`) - .join('\n') - return glob.hashFiles(combinedPatterns, {followSymbolicLinks}) -} - export function truncateArgs(args: string): string { return args.trim().replace(/\s+/g, ' ').substr(0, 400) } diff --git a/src/cache-wrapper.ts b/src/cache-wrapper.ts deleted file mode 100644 index 981f5c8..0000000 --- a/src/cache-wrapper.ts +++ /dev/null @@ -1,132 +0,0 @@ -import * as path from 'path' -import * as fs from 'fs' -import * as os from 'os' - -import * as core from '@actions/core' -import * as cache from '@actions/cache' - -const WRAPPER_SLUG = 'WRAPPER_SLUG' - -export async function restoreCachedWrapperDist( - gradlewDirectory: string | null -): Promise { - if (isWrapperCacheDisabled()) return - if (gradlewDirectory == null) return - - const wrapperProperties = path.join( - path.resolve(gradlewDirectory), - 'gradle/wrapper/gradle-wrapper.properties' - ) - const wrapperSlug = extractGradleWrapperSlugFrom(wrapperProperties) - if (!wrapperSlug) { - core.warning( - `Could not calculate wrapper version from ${wrapperProperties}` - ) - return - } - - const wrapperDir = getWrapperDir(wrapperSlug) - const cacheKey = getCacheKey(wrapperSlug) - const cachePath = getCachePath(wrapperSlug) - - // Check if the wrapper has already been downloaded to Gradle User Home - if (fs.existsSync(wrapperDir)) return - - try { - const restoredKey = await cache.restoreCache([cachePath], cacheKey) - - if (restoredKey) { - core.info( - `Wrapper installation restored from cache key: ${restoredKey}` - ) - } else { - core.info( - `Wrapper installation cache not found. Will download and cache with key: ${cacheKey}.` - ) - // Save the slug to trigger caching of the downloaded wrapper - core.saveState(WRAPPER_SLUG, wrapperSlug) - } - } catch (error) { - core.info( - `Wrapper installation cache restore failed for key: ${cacheKey}.\n ${error}` - ) - } -} - -export async function cacheWrapperDist(): Promise { - if (isWrapperCacheDisabled()) return - - const wrapperSlug = core.getState(WRAPPER_SLUG) - if (!wrapperSlug) return - - const wrapperDir = getWrapperDir(wrapperSlug) - const cacheKey = getCacheKey(wrapperSlug) - const cachePath = getCachePath(wrapperSlug) - - if (!fs.existsSync(wrapperDir)) { - core.warning(`No wrapper installation to cache at ${wrapperDir}`) - return - } - - core.info(`Will cache wrapper zip ${cachePath} with key ${cacheKey}`) - - try { - await cache.saveCache([cachePath], cacheKey) - } catch (error) { - if (error.name === cache.ValidationError.name) { - throw error - } else if (error.name === cache.ReserveCacheError.name) { - core.info(error.message) - } else { - core.info(`[warning] ${error.message}`) - } - } - - return -} - -export function extractGradleWrapperSlugFrom( - wrapperProperties: string -): string | null { - const props = fs.readFileSync(wrapperProperties, {encoding: 'utf8'}) - const distUrlLine = props - .split('\n') - .find(line => line.startsWith('distributionUrl')) - if (!distUrlLine) return null - return extractGradleWrapperSlugFromDistUri(distUrlLine.substr(16).trim()) -} - -export function extractGradleWrapperSlugFromDistUri( - distUri: string -): string | null { - const regex = /.*gradle-(.*-(bin|all))\.zip/ - const match = distUri.match(regex) - return match ? match[1] : null -} - -function isWrapperCacheDisabled(): boolean { - // Check if either 'distributions' or 'wrapper' cache has been disabled - const wrapperCacheEnabled = core.getBooleanInput('wrapper-cache-enabled') - const distributionsCacheEnabled = core.getBooleanInput( - 'distributions-cache-enabled' - ) - return !wrapperCacheEnabled || !distributionsCacheEnabled -} - -function getCacheKey(wrapperSlug: string): string { - return `wrapper-v1-${wrapperSlug}` -} - -function getWrapperDir(wrapperSlug: string): string { - return path.resolve( - os.homedir(), - `.gradle/wrapper/dists/gradle-${wrapperSlug}` - ) -} - -function getCachePath(wrapperSlug: string): string { - return path.resolve( - os.homedir(), - `.gradle/wrapper/dists/gradle-${wrapperSlug}/*/gradle-${wrapperSlug}.zip` - ) -}