mirror of
https://github.com/gradle/actions.git
synced 2026-09-05 04:21:37 +00:00
Add cache-provider: external to skip Gradle User Home caching (#1059)
Users relying on an external mechanism to save/restore Gradle User Home (e.g. Develocity Artifact Cache) previously had to set cache-disabled: true, which is confusing since caching isn't actually disabled — it's just not managed by this action — and the Job Summary misleadingly reported caching as "Disabled". cache-provider: external skips Gradle User Home restore/save (same as cache-disabled) but reports a distinct "External" status in the Job Summary, explaining that caching is handled by another provider. --------- Co-authored-by: Claude Sonnet 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
4ca152ae04
commit
910b061d4d
@@ -132,6 +132,23 @@ jobs:
|
|||||||
working-directory: .github/workflow-samples/kotlin-dsl
|
working-directory: .github/workflow-samples/kotlin-dsl
|
||||||
run: ./gradlew assemble
|
run: ./gradlew assemble
|
||||||
|
|
||||||
|
cache-provider-external:
|
||||||
|
needs: build-distribution
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
- name: Initialize integ-test
|
||||||
|
uses: ./.github/actions/init-integ-test
|
||||||
|
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: ./setup-gradle
|
||||||
|
with:
|
||||||
|
cache-provider: external
|
||||||
|
- name: Build kotlin-dsl project
|
||||||
|
working-directory: .github/workflow-samples/kotlin-dsl
|
||||||
|
run: ./gradlew assemble
|
||||||
|
|
||||||
basic-caching:
|
basic-caching:
|
||||||
needs: build-distribution
|
needs: build-distribution
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -134,6 +134,30 @@ jobs:
|
|||||||
script: |
|
script: |
|
||||||
core.setFailed('No Build Scan detected')
|
core.setFailed('No Build Scan detected')
|
||||||
|
|
||||||
|
# Test that build scans are captured when caching is external
|
||||||
|
caching-config-cache-provider-external:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
- name: Initialize integ-test
|
||||||
|
uses: ./.github/actions/init-integ-test
|
||||||
|
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: ./setup-gradle
|
||||||
|
with:
|
||||||
|
cache-provider: external
|
||||||
|
- name: Build using Gradle wrapper
|
||||||
|
id: gradle
|
||||||
|
working-directory: .github/workflow-samples/groovy-dsl
|
||||||
|
run: ./gradlew help
|
||||||
|
- name: Check Build Scan url is captured
|
||||||
|
if: ${{ !steps.gradle.outputs.build-scan-url }}
|
||||||
|
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
core.setFailed('No Build Scan detected')
|
||||||
|
|
||||||
# Test seed the cache with cache-write-only and verify with cache-read-only
|
# Test seed the cache with cache-write-only and verify with cache-read-only
|
||||||
caching-config-seed-write-only:
|
caching-config-seed-write-only:
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ inputs:
|
|||||||
Specifies the caching implementation to use.
|
Specifies the caching implementation to use.
|
||||||
'enhanced' (default) uses the full-featured commercial caching service (gradle-actions-caching).
|
'enhanced' (default) uses the full-featured commercial caching service (gradle-actions-caching).
|
||||||
'basic' uses a simple open-source caching implementation based on GitHub Actions cache.
|
'basic' uses a simple open-source caching implementation based on GitHub Actions cache.
|
||||||
|
'external' disables Gradle User Home caching by this action, for use when Gradle User Home is already saved and restored by another mechanism (e.g. Develocity Artifact Cache).
|
||||||
required: false
|
required: false
|
||||||
default: 'enhanced'
|
default: 'enhanced'
|
||||||
|
|
||||||
|
|||||||
+12
-1
@@ -133,6 +133,7 @@ You choose which provider to use via the `cache-provider` input:
|
|||||||
|
|
||||||
- **`enhanced`** (default): Uses the full-featured commercial `gradle-actions-caching` library. Provides advanced features like fine-grained cache entries, intelligent cache cleanup, and deduplication. See [Enhanced Caching](#enhanced-caching) for details.
|
- **`enhanced`** (default): Uses the full-featured commercial `gradle-actions-caching` library. Provides advanced features like fine-grained cache entries, intelligent cache cleanup, and deduplication. See [Enhanced Caching](#enhanced-caching) for details.
|
||||||
- **`basic`**: A fully open-source (MIT) caching implementation built on the standard GitHub Actions cache (`@actions/cache`). Uses the same caching strategy as `actions/setup-java` with `cache: gradle`. See [Basic Caching](#basic-caching) for details.
|
- **`basic`**: A fully open-source (MIT) caching implementation built on the standard GitHub Actions cache (`@actions/cache`). Uses the same caching strategy as `actions/setup-java` with `cache: gradle`. See [Basic Caching](#basic-caching) for details.
|
||||||
|
- **`external`**: Disables Gradle User Home caching by this action, for use when another mechanism already saves and restores Gradle User Home. See [Using an external cache provider](#using-an-external-cache-provider) for details.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# Use the open-source basic cache provider
|
# Use the open-source basic cache provider
|
||||||
@@ -185,7 +186,17 @@ Specifically:
|
|||||||
- Avoid using `actions/cache` configured to cache the Gradle User Home, [as described in this example](https://github.com/actions/cache/blob/main/examples.md#java---gradle).
|
- Avoid using `actions/cache` configured to cache the Gradle User Home, [as described in this example](https://github.com/actions/cache/blob/main/examples.md#java---gradle).
|
||||||
- Avoid using `actions/setup-java` with the `cache: gradle` option, [as described here](https://github.com/actions/setup-java#caching-gradle-dependencies).
|
- Avoid using `actions/setup-java` with the `cache: gradle` option, [as described here](https://github.com/actions/setup-java#caching-gradle-dependencies).
|
||||||
|
|
||||||
Using either of these mechanisms may interfere with the caching provided by this action. If you choose to use a different mechanism to save and restore the Gradle User Home, you should disable the caching provided by this action, as described above.
|
Using either of these mechanisms may interfere with the caching provided by this action. If you choose to use a different mechanism to save and restore the Gradle User Home, you should disable the caching provided by this action, as described above, or [use `cache-provider: external`](#using-an-external-cache-provider) if that mechanism is a dedicated Gradle User Home cache.
|
||||||
|
|
||||||
|
#### Using an external cache provider
|
||||||
|
|
||||||
|
If Gradle User Home is already saved and restored by another mechanism — for example the [Develocity Artifact Cache](https://github.com/gradle/actions-caching) — set `cache-provider: external` rather than `cache-disabled: true`.
|
||||||
|
|
||||||
|
Both settings stop `setup-gradle` from restoring or saving Gradle User Home, but `cache-provider: external` reflects the actual reason: caching is still happening, just not through this action. It also updates the Job Summary to say that caching was handled externally, rather than implying that caching was turned off.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
cache-provider: external
|
||||||
|
```
|
||||||
|
|
||||||
## Enhanced Caching
|
## Enhanced Caching
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ inputs:
|
|||||||
Specifies the caching implementation to use.
|
Specifies the caching implementation to use.
|
||||||
'enhanced' (default) uses the full-featured commercial caching service (gradle-actions-caching).
|
'enhanced' (default) uses the full-featured commercial caching service (gradle-actions-caching).
|
||||||
'basic' uses a simple open-source caching implementation based on GitHub Actions cache.
|
'basic' uses a simple open-source caching implementation based on GitHub Actions cache.
|
||||||
|
'external' disables Gradle User Home caching by this action, for use when Gradle User Home is already saved and restored by another mechanism (e.g. Develocity Artifact Cache).
|
||||||
required: false
|
required: false
|
||||||
default: 'enhanced'
|
default: 'enhanced'
|
||||||
|
|
||||||
|
|||||||
@@ -5,14 +5,18 @@ import {pathToFileURL} from 'url'
|
|||||||
import {CacheConfig, CacheProvider} from './configuration'
|
import {CacheConfig, CacheProvider} from './configuration'
|
||||||
import {BasicCacheService} from './cache-service-basic'
|
import {BasicCacheService} from './cache-service-basic'
|
||||||
import {BuildResult} from './build-results'
|
import {BuildResult} from './build-results'
|
||||||
import {CacheOptions, CacheReport, CacheService} from './cache-service'
|
import {CacheOptions, CacheReport, CacheService, CacheStatus} from './cache-service'
|
||||||
import {ProviderNote} from './caching-report'
|
import {ProviderNote} from './caching-report'
|
||||||
|
|
||||||
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.`
|
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.`
|
||||||
|
|
||||||
const BASIC_CACHE_MESSAGE = `Basic Caching: This build uses the basic open-source caching provider. 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_MESSAGE = `Basic Caching: This build uses the basic open-source caching provider. 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 EXTERNAL_CACHE_MESSAGE = `External Caching: Gradle User Home is managed by an external caching provider. This action will not restore or save Gradle User Home.`
|
||||||
|
|
||||||
class NoOpCacheService implements CacheService {
|
class NoOpCacheService implements CacheService {
|
||||||
|
constructor(private readonly status: CacheStatus) {}
|
||||||
|
|
||||||
async restore(_gradleUserHome: string, _cacheOptions: CacheOptions): Promise<void> {
|
async restore(_gradleUserHome: string, _cacheOptions: CacheOptions): Promise<void> {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -22,14 +26,19 @@ class NoOpCacheService implements CacheService {
|
|||||||
_buildResults: BuildResult[],
|
_buildResults: BuildResult[],
|
||||||
_cacheOptions: CacheOptions
|
_cacheOptions: CacheOptions
|
||||||
): Promise<CacheReport> {
|
): Promise<CacheReport> {
|
||||||
return {status: 'disabled', entries: []}
|
return {status: this.status, entries: []}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getCacheService(cacheConfig: CacheConfig): Promise<CacheService> {
|
export async function getCacheService(cacheConfig: CacheConfig): Promise<CacheService> {
|
||||||
|
if (cacheConfig.getCacheProvider() === CacheProvider.External) {
|
||||||
|
logCacheMessage(EXTERNAL_CACHE_MESSAGE)
|
||||||
|
return new NoOpCacheService('external')
|
||||||
|
}
|
||||||
|
|
||||||
if (cacheConfig.isCacheDisabled()) {
|
if (cacheConfig.isCacheDisabled()) {
|
||||||
logCacheMessage('Cache is disabled: will not restore state from previous builds.')
|
logCacheMessage('Cache is disabled: will not restore state from previous builds.')
|
||||||
return new NoOpCacheService()
|
return new NoOpCacheService('disabled')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cacheConfig.getCacheProvider() === CacheProvider.Basic) {
|
if (cacheConfig.getCacheProvider() === CacheProvider.Basic) {
|
||||||
@@ -43,10 +52,10 @@ export async function getCacheService(cacheConfig: CacheConfig): Promise<CacheSe
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Identifies the caching provider for the Job Summary. Returns `undefined` when
|
* Identifies the caching provider for the Job Summary. Returns `undefined` when
|
||||||
* caching is disabled, since no provider is engaged in that case.
|
* caching is disabled or managed externally, since no provider is engaged in that case.
|
||||||
*/
|
*/
|
||||||
export function getProviderNote(cacheConfig: CacheConfig): ProviderNote | undefined {
|
export function getProviderNote(cacheConfig: CacheConfig): ProviderNote | undefined {
|
||||||
if (cacheConfig.isCacheDisabled()) {
|
if (cacheConfig.isCacheDisabled() || cacheConfig.getCacheProvider() === CacheProvider.External) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
return cacheConfig.getCacheProvider() === CacheProvider.Basic ? {kind: 'basic'} : {kind: 'enhanced'}
|
return cacheConfig.getCacheProvider() === CacheProvider.Basic ? {kind: 'basic'} : {kind: 'enhanced'}
|
||||||
|
|||||||
@@ -15,10 +15,20 @@ export interface CacheOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type CacheStatus =
|
export type CacheStatus =
|
||||||
'enabled' | 'read-only' | 'write-only' | 'disabled' | 'disabled-existing-home' | 'not-available'
|
| 'enabled' // Gradle User Home was restored from and saved to the cache
|
||||||
|
| 'read-only' // restored from the cache, but not saved
|
||||||
|
| 'write-only' // saved to the cache, but not restored
|
||||||
|
| 'disabled' // caching was turned off via the cache-disabled parameter
|
||||||
|
| 'disabled-existing-home' // a pre-existing Gradle User Home was found, so caching was skipped
|
||||||
|
| 'not-available' // the GitHub Actions cache service could not be reached
|
||||||
|
| 'external' // Gradle User Home is cached by an external provider, not by this action
|
||||||
|
|
||||||
export type CacheCleanupStatus =
|
export type CacheCleanupStatus =
|
||||||
'enabled' | 'disabled-param' | 'disabled-failure' | 'disabled-config-cache-hit' | 'disabled-readonly'
|
| 'enabled' // stale files were purged from Gradle User Home before saving
|
||||||
|
| 'disabled-param' // disabled via action parameter
|
||||||
|
| 'disabled-failure' // skipped due to a build failure
|
||||||
|
| 'disabled-config-cache-hit' // skipped due to configuration-cache reuse
|
||||||
|
| 'disabled-readonly' // always disabled when the cache is read-only
|
||||||
|
|
||||||
export type ProjectCacheStatus =
|
export type ProjectCacheStatus =
|
||||||
| 'not-enabled' // the hidden opt-in env var was not set (rendered as nothing)
|
| 'not-enabled' // the hidden opt-in env var was not set (rendered as nothing)
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ const STATUS_COPY: Record<CacheStatus, string> = {
|
|||||||
'write-only': `[Cache was write-only](${DOCS}#using-the-cache-write-only) — Gradle User Home was not restored from the cache.`,
|
'write-only': `[Cache was write-only](${DOCS}#using-the-cache-write-only) — Gradle User Home was not restored from the cache.`,
|
||||||
disabled: `[Caching was disabled](${DOCS}#disabling-caching) — Gradle User Home was not restored from or saved to the cache.`,
|
disabled: `[Caching was disabled](${DOCS}#disabling-caching) — Gradle User Home was not restored from or saved to the cache.`,
|
||||||
'disabled-existing-home': `⚠️ [Caching was skipped](${DOCS}#overwriting-an-existing-gradle-user-home) — a pre-existing Gradle User Home was found, so the cache was not restored or saved.`,
|
'disabled-existing-home': `⚠️ [Caching was skipped](${DOCS}#overwriting-an-existing-gradle-user-home) — a pre-existing Gradle User Home was found, so the cache was not restored or saved.`,
|
||||||
'not-available': `Caching is not available — the GitHub Actions cache service could not be reached, so Gradle User Home was not restored or saved.`
|
'not-available': `Caching is not available — the GitHub Actions cache service could not be reached, so Gradle User Home was not restored or saved.`,
|
||||||
|
external: `[Gradle User Home is cached externally](${DOCS}#using-an-external-cache-provider) — this action did not restore or save the Gradle User Home.`
|
||||||
}
|
}
|
||||||
|
|
||||||
const CLEANUP_COPY: Record<CacheCleanupStatus, string> = {
|
const CLEANUP_COPY: Record<CacheCleanupStatus, string> = {
|
||||||
@@ -63,7 +64,13 @@ function isActive(status: CacheStatus): boolean {
|
|||||||
function renderHeading(status: CacheStatus, providerNote?: ProviderNote): string {
|
function renderHeading(status: CacheStatus, providerNote?: ProviderNote): string {
|
||||||
if (!isActive(status)) {
|
if (!isActive(status)) {
|
||||||
const label =
|
const label =
|
||||||
status === 'disabled-existing-home' ? 'Skipped' : status === 'not-available' ? 'Unavailable' : 'Disabled'
|
status === 'disabled-existing-home'
|
||||||
|
? 'Skipped'
|
||||||
|
: status === 'not-available'
|
||||||
|
? 'Unavailable'
|
||||||
|
: status === 'external'
|
||||||
|
? 'External'
|
||||||
|
: 'Disabled'
|
||||||
return `<h4>Gradle State Caching - ${label}</h4>`
|
return `<h4>Gradle State Caching - ${label}</h4>`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -175,14 +175,19 @@ export class CacheConfig {
|
|||||||
case 'enhanced':
|
case 'enhanced':
|
||||||
case '':
|
case '':
|
||||||
return CacheProvider.Enhanced
|
return CacheProvider.Enhanced
|
||||||
|
case 'external':
|
||||||
|
return CacheProvider.External
|
||||||
}
|
}
|
||||||
throw TypeError(`The value '${val}' is not valid for 'cache-provider'. Valid values are: [basic, enhanced].`)
|
throw TypeError(
|
||||||
|
`The value '${val}' is not valid for 'cache-provider'. Valid values are: [basic, enhanced, external].`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum CacheProvider {
|
export enum CacheProvider {
|
||||||
Basic = 'basic',
|
Basic = 'basic',
|
||||||
Enhanced = 'enhanced'
|
Enhanced = 'enhanced',
|
||||||
|
External = 'external'
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum CacheCleanupOption {
|
export enum CacheCleanupOption {
|
||||||
|
|||||||
@@ -45,6 +45,51 @@ describe('getCacheService selection logic', () => {
|
|||||||
expect(service).toBeInstanceOf(BasicCacheService)
|
expect(service).toBeInstanceOf(BasicCacheService)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('returns NoOpCacheService reporting "external" when cache-provider is external', async () => {
|
||||||
|
const {getCacheService} = await import('../../src/cache-service-loader')
|
||||||
|
const mockConfig = {
|
||||||
|
isCacheDisabled: () => false,
|
||||||
|
getCacheProvider: () => CacheProvider.External
|
||||||
|
} as unknown as CacheConfig
|
||||||
|
|
||||||
|
const service = await getCacheService(mockConfig)
|
||||||
|
const report = await service.save('/home/.gradle', [], {
|
||||||
|
disabled: false,
|
||||||
|
readOnly: false,
|
||||||
|
writeOnly: false,
|
||||||
|
overwriteExisting: false,
|
||||||
|
strictMatch: false,
|
||||||
|
cleanup: 'never',
|
||||||
|
includes: [],
|
||||||
|
excludes: []
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(report.status).toBe('external')
|
||||||
|
expect(report.entries).toHaveLength(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('reports "external" even when cache-disabled is also true', async () => {
|
||||||
|
const {getCacheService} = await import('../../src/cache-service-loader')
|
||||||
|
const mockConfig = {
|
||||||
|
isCacheDisabled: () => true,
|
||||||
|
getCacheProvider: () => CacheProvider.External
|
||||||
|
} as unknown as CacheConfig
|
||||||
|
|
||||||
|
const service = await getCacheService(mockConfig)
|
||||||
|
const report = await service.save('/home/.gradle', [], {
|
||||||
|
disabled: false,
|
||||||
|
readOnly: false,
|
||||||
|
writeOnly: false,
|
||||||
|
overwriteExisting: false,
|
||||||
|
strictMatch: false,
|
||||||
|
cleanup: 'never',
|
||||||
|
includes: [],
|
||||||
|
excludes: []
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(report.status).toBe('external')
|
||||||
|
})
|
||||||
|
|
||||||
describe('getProviderNote', () => {
|
describe('getProviderNote', () => {
|
||||||
it('returns undefined when cache is disabled', async () => {
|
it('returns undefined when cache is disabled', async () => {
|
||||||
const {getProviderNote} = await import('../../src/cache-service-loader')
|
const {getProviderNote} = await import('../../src/cache-service-loader')
|
||||||
@@ -56,6 +101,16 @@ describe('getCacheService selection logic', () => {
|
|||||||
expect(getProviderNote(mockConfig)).toBeUndefined()
|
expect(getProviderNote(mockConfig)).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('returns undefined when cache-provider is external', async () => {
|
||||||
|
const {getProviderNote} = await import('../../src/cache-service-loader')
|
||||||
|
const mockConfig = {
|
||||||
|
isCacheDisabled: () => false,
|
||||||
|
getCacheProvider: () => CacheProvider.External
|
||||||
|
} as unknown as CacheConfig
|
||||||
|
|
||||||
|
expect(getProviderNote(mockConfig)).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
it('returns basic note for the basic provider', async () => {
|
it('returns basic note for the basic provider', async () => {
|
||||||
const {getProviderNote} = await import('../../src/cache-service-loader')
|
const {getProviderNote} = await import('../../src/cache-service-loader')
|
||||||
const mockConfig = {
|
const mockConfig = {
|
||||||
|
|||||||
@@ -142,4 +142,14 @@ describe('renderCachingReport', () => {
|
|||||||
expect(md).toContain('<h4>Gradle State Caching - Unavailable</h4>')
|
expect(md).toContain('<h4>Gradle State Caching - Unavailable</h4>')
|
||||||
expect(md).not.toContain('<details>')
|
expect(md).not.toContain('<details>')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('renders a compact external report with no provider note', () => {
|
||||||
|
const report: CacheReport = {status: 'external', entries: []}
|
||||||
|
const md = renderCachingReport(report, undefined)
|
||||||
|
|
||||||
|
expect(md).toContain('<h4>Gradle State Caching - External</h4>')
|
||||||
|
expect(md).toContain('cached externally')
|
||||||
|
expect(md).not.toContain('<details>')
|
||||||
|
expect(md).not.toContain('DISTRIBUTION.md')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user