mirror of
https://github.com/gradle/actions.git
synced 2026-04-19 18:12:58 +08:00
Add open-source 'basic' cache provider and revamp licensing documentation (#930)
## Summary
- **New `basic` cache provider**: Adds an open-source (MIT-licensed)
caching implementation built on `@actions/cache` as an alternative to
the proprietary Enhanced Caching. Users can opt in with `cache-provider:
basic` on both `setup-gradle` and `dependency-submission` actions.
- **Revamped licensing & distribution docs**: Replaces the verbose
licensing notice block (previously shown in README, docs, and logs) with
a friendlier callout and a new dedicated
[DISTRIBUTION.md](./DISTRIBUTION.md) covering component licensing, usage
tiers, data privacy ("Safe Harbor"), and opt-out instructions.
- **Improved messaging**: Enhanced Caching and Basic Caching each
display concise, informative log messages and job summary notes instead
of the previous wall-of-text license warning.
- **New integration tests**: Adds `integ-test-basic-cache-provider.yml`
workflow that seeds and verifies the basic cache provider across
platforms, plus unit tests for `BasicCacheService` and `getCacheService`
selection logic.
- **CI workflow reorganization**: Dependency-submission integration
tests extracted into their own reusable suite
(`suite-integ-test-dependency-submission.yml`); sample project tests
moved into the caching suite.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
import * as cache from '@actions/cache'
|
||||
import * as core from '@actions/core'
|
||||
import * as glob from '@actions/glob'
|
||||
import * as path from 'path'
|
||||
|
||||
import {BuildResult} from './build-results'
|
||||
import {CacheOptions, CacheService} from './cache-service'
|
||||
|
||||
const PRIMARY_KEY_STATE = 'BASIC_CACHE_PRIMARY_KEY'
|
||||
const RESTORED_KEY_STATE = 'BASIC_CACHE_RESTORED_KEY'
|
||||
const CACHE_KEY_PREFIX = 'setup-java'
|
||||
|
||||
const GRADLE_BUILD_FILE_PATTERNS = [
|
||||
'**/*.gradle*',
|
||||
'**/gradle-wrapper.properties',
|
||||
'buildSrc/**/Versions.kt',
|
||||
'buildSrc/**/Dependencies.kt',
|
||||
'gradle/*.versions.toml',
|
||||
'**/versions.properties'
|
||||
]
|
||||
|
||||
export class BasicCacheService implements CacheService {
|
||||
async restore(gradleUserHome: string, _cacheOptions: CacheOptions): Promise<void> {
|
||||
const cachePaths = getCachePaths(gradleUserHome)
|
||||
const primaryKey = await computeCacheKey()
|
||||
core.saveState(PRIMARY_KEY_STATE, primaryKey)
|
||||
|
||||
// No "restoreKeys" is set, to start with a clear cache after dependency update
|
||||
// See https://github.com/actions/setup-java/issues/269
|
||||
try {
|
||||
const restoredKey = await cache.restoreCache(cachePaths, primaryKey)
|
||||
if (restoredKey) {
|
||||
core.saveState(RESTORED_KEY_STATE, restoredKey)
|
||||
core.info(`Basic caching restored from cache key: ${restoredKey}`)
|
||||
} else {
|
||||
core.info('Basic caching did not find an entry to restore. Will start with empty state.')
|
||||
}
|
||||
} catch (error) {
|
||||
core.warning(`Basic caching failed to restore from cache: ${error}`)
|
||||
}
|
||||
}
|
||||
|
||||
async save(gradleUserHome: string, _buildResults: BuildResult[], cacheOptions: CacheOptions): Promise<string> {
|
||||
if (cacheOptions.readOnly) {
|
||||
const restoredKey = core.getState(RESTORED_KEY_STATE)
|
||||
if (restoredKey) {
|
||||
return `Basic caching was read-only. Restored from cache key \`${restoredKey}\`.`
|
||||
}
|
||||
return 'Basic caching was read-only. No cache entry was found to restore.'
|
||||
}
|
||||
|
||||
const primaryKey = core.getState(PRIMARY_KEY_STATE)
|
||||
const restoredKey = core.getState(RESTORED_KEY_STATE)
|
||||
|
||||
if (restoredKey === primaryKey) {
|
||||
core.info(`Basic caching restored entry with key \`${primaryKey}\`. Save was skipped.`)
|
||||
return `Basic caching restored entry with key \`${primaryKey}\`. Save was skipped.`
|
||||
}
|
||||
|
||||
const cachePaths = getCachePaths(gradleUserHome)
|
||||
|
||||
try {
|
||||
await cache.saveCache(cachePaths, primaryKey)
|
||||
core.info(`Basic caching saved entry with key: ${primaryKey}`)
|
||||
return `Basic caching saved entry with key \`${primaryKey}\`.`
|
||||
} catch (error) {
|
||||
core.warning(`Basic caching failed to save entry with key \`${primaryKey}\`: ${error}`)
|
||||
return `Basic caching save failed: ${error}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getCachePaths(gradleUserHome: string): string[] {
|
||||
return [path.join(gradleUserHome, 'caches'), path.join(gradleUserHome, 'wrapper')]
|
||||
}
|
||||
|
||||
async function computeCacheKey(): Promise<string> {
|
||||
const fileHash = await glob.hashFiles(GRADLE_BUILD_FILE_PATTERNS.join('\n'))
|
||||
if (!fileHash) {
|
||||
throw new Error(
|
||||
`No file in ${process.cwd()} matched to [${GRADLE_BUILD_FILE_PATTERNS}], make sure you have checked out the target repository`
|
||||
)
|
||||
}
|
||||
return `${CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}-gradle-${fileHash}`
|
||||
}
|
||||
@@ -2,7 +2,8 @@ import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import {pathToFileURL} from 'url'
|
||||
|
||||
import {CacheConfig} from './configuration'
|
||||
import {CacheConfig, CacheProvider} from './configuration'
|
||||
import {BasicCacheService} from './cache-service-basic'
|
||||
import {BuildResult} from './build-results'
|
||||
import {CacheOptions, CacheService} from './cache-service'
|
||||
|
||||
@@ -10,36 +11,24 @@ const NOOP_CACHING_REPORT = `
|
||||
[Cache was disabled](https://github.com/gradle/actions/blob/main/docs/setup-gradle.md#disabling-caching). Gradle User Home was not restored from or saved to the cache.
|
||||
`
|
||||
|
||||
const CACHE_LICENSE_WARNING = `
|
||||
***********************************************************
|
||||
LICENSING NOTICE
|
||||
const ENHANCED_CACHE_MESSAGE = `Enhanced Caching: This build is using the proprietary 'gradle-actions-caching' provider for optimized caching support. See https://github.com/gradle/actions/blob/main/DISTRIBUTION.md for terms of use and opt-out instructions.`
|
||||
|
||||
The caching functionality in \`gradle-actions\` has been extracted into \`gradle-actions-caching\`, a proprietary commercial component that is not covered by the MIT License.
|
||||
The bundled \`gradle-actions-caching\` component is licensed and governed by a separate license, available at https://gradle.com/legal/terms-of-use/.
|
||||
|
||||
The \`gradle-actions-caching\` component is used only when caching is enabled and is not loaded or used when caching is disabled.
|
||||
|
||||
Use of the \`gradle-actions-caching\` component is subject to a separate license, available at https://gradle.com/legal/terms-of-use/.
|
||||
If you do not agree to these license terms, do not use the \`gradle-actions-caching\` component.
|
||||
|
||||
You can suppress this message by accepting the terms in your action configuration: see https://github.com/gradle/actions/blob/main/README.md
|
||||
***********************************************************
|
||||
const ENHANCED_CACHE_SUMMARY = `
|
||||
> [!NOTE]
|
||||
> ### ⚡️ Enhanced Caching enabled
|
||||
> This build provides optimized caching support via the proprietary **gradle-actions-caching** provider.
|
||||
> See [DISTRIBUTION.md](https://github.com/gradle/actions/blob/main/DISTRIBUTION.md) for terms of use and opt-out instructions.
|
||||
`
|
||||
|
||||
const CACHE_LICENSE_SUMMARY = `
|
||||
> [!IMPORTANT]
|
||||
> #### Licensing notice
|
||||
>
|
||||
> The caching functionality in \`gradle-actions\` has been extracted into \`gradle-actions-caching\`, a proprietary commercial component that is not covered by the MIT License.
|
||||
> The bundled \`gradle-actions-caching\` component is licensed and governed by a separate license, available at https://gradle.com/legal/terms-of-use/.
|
||||
>
|
||||
> The \`gradle-actions-caching\` component is used only when caching is enabled and is not loaded or used when caching is disabled.
|
||||
>
|
||||
> Use of the \`gradle-actions-caching\` component is subject to a separate license, available at https://gradle.com/legal/terms-of-use/.
|
||||
> If you do not agree to these license terms, do not use the \`gradle-actions-caching\` component.
|
||||
>
|
||||
>You can suppress this message by [accepting the terms in your action configuration](https://github.com/gradle/actions/blob/main/README.md).
|
||||
`
|
||||
const BASIC_CACHE_MESSAGE = `Basic Caching: This build uses the open-source caching provider for reliable, path-based caching of Gradle dependencies. Upgrade available: for faster builds and advanced features, consider switching to the Enhanced Caching provider. See https://github.com/gradle/actions/blob/main/DISTRIBUTION.md for details.`
|
||||
|
||||
const BASIC_CACHE_SUMMARY = `
|
||||
> [!NOTE]
|
||||
> ### 🛡️ Basic Caching enabled
|
||||
> This build uses the open-source caching provider for reliable, path-based caching of Gradle dependencies.
|
||||
>
|
||||
> **Upgrade Available:** For faster builds and advanced features, consider switching to the **Enhanced Caching** provider.
|
||||
> See [DISTRIBUTION.md](https://github.com/gradle/actions/blob/main/DISTRIBUTION.md) for details.`
|
||||
|
||||
class NoOpCacheService implements CacheService {
|
||||
async restore(_gradleUserHome: string, _cacheOptions: CacheOptions): Promise<void> {
|
||||
@@ -53,9 +42,11 @@ class NoOpCacheService implements CacheService {
|
||||
|
||||
class LicenseWarningCacheService implements CacheService {
|
||||
private delegate: CacheService
|
||||
private summary: string
|
||||
|
||||
constructor(delegate: CacheService) {
|
||||
constructor(delegate: CacheService, summary: string) {
|
||||
this.delegate = delegate
|
||||
this.summary = summary
|
||||
}
|
||||
|
||||
async restore(gradleUserHome: string, cacheOptions: CacheOptions): Promise<void> {
|
||||
@@ -64,22 +55,28 @@ class LicenseWarningCacheService implements CacheService {
|
||||
|
||||
async save(gradleUserHome: string, buildResults: BuildResult[], cacheOptions: CacheOptions): Promise<string> {
|
||||
const cachingReport = await this.delegate.save(gradleUserHome, buildResults, cacheOptions)
|
||||
return `${cachingReport}\n${CACHE_LICENSE_SUMMARY}`
|
||||
return `${cachingReport}\n${this.summary}`
|
||||
}
|
||||
}
|
||||
|
||||
export async function getCacheService(cacheConfig: CacheConfig): Promise<CacheService> {
|
||||
if (cacheConfig.isCacheDisabled()) {
|
||||
logCacheMessage('Cache is disabled: will not restore state from previous builds.')
|
||||
return new NoOpCacheService()
|
||||
}
|
||||
|
||||
if (cacheConfig.getCacheProvider() === CacheProvider.Basic) {
|
||||
logCacheMessage(BASIC_CACHE_MESSAGE)
|
||||
return new LicenseWarningCacheService(new BasicCacheService(), BASIC_CACHE_SUMMARY)
|
||||
}
|
||||
|
||||
logCacheMessage(ENHANCED_CACHE_MESSAGE)
|
||||
const cacheService = await loadVendoredCacheService()
|
||||
if (cacheConfig.isCacheLicenseAccepted()) {
|
||||
return cacheService
|
||||
}
|
||||
|
||||
await logCacheLicenseWarning()
|
||||
return new LicenseWarningCacheService(cacheService)
|
||||
return new LicenseWarningCacheService(cacheService, ENHANCED_CACHE_SUMMARY)
|
||||
}
|
||||
|
||||
export async function loadVendoredCacheService(): Promise<CacheService> {
|
||||
@@ -99,6 +96,6 @@ function findVendoredLibraryPath(): string {
|
||||
throw new Error(`Unable to locate vendored cache library at ${absolutePath}.`)
|
||||
}
|
||||
|
||||
export async function logCacheLicenseWarning(): Promise<void> {
|
||||
console.info(CACHE_LICENSE_WARNING)
|
||||
export function logCacheMessage(message: string): void {
|
||||
console.info(message)
|
||||
}
|
||||
|
||||
@@ -171,6 +171,23 @@ export class CacheConfig {
|
||||
const dvConfig = new DevelocityConfig()
|
||||
return dvConfig.getDevelocityAccessKey() !== '' || dvConfig.hasTermsOfUseAgreement()
|
||||
}
|
||||
|
||||
getCacheProvider(): CacheProvider {
|
||||
const val = core.getInput('cache-provider')
|
||||
switch (val.toLowerCase().trim()) {
|
||||
case 'basic':
|
||||
return CacheProvider.Basic
|
||||
case 'enhanced':
|
||||
case '':
|
||||
return CacheProvider.Enhanced
|
||||
}
|
||||
throw TypeError(`The value '${val}' is not valid for 'cache-provider'. Valid values are: [basic, enhanced].`)
|
||||
}
|
||||
}
|
||||
|
||||
export enum CacheProvider {
|
||||
Basic = 'basic',
|
||||
Enhanced = 'enhanced'
|
||||
}
|
||||
|
||||
export enum CacheCleanupOption {
|
||||
|
||||
Reference in New Issue
Block a user