Files
actions/sources/src/cache-service-basic.ts
T
Daz DeBoer 97715a29bc Redesign the caching Job Summary (#985)
Redesigns the caching section of the Job Summary into a single,
consistent layout across every cache provider and state, and integrates
the provider message into the report rather than appending it
disconnected at the bottom.

## Motivation

The caching report was produced by three divergent code paths (NoOp /
basic / enhanced), each rendering its own markdown:

- **Explicitly disabled** → a one-line message, no expand, no provider
note.
- **Enhanced** (incl. skipped-due-to-existing-home) → a full `<details>`
block.
- **Basic** → a one-line message with **no** expandable details at all.

The Enhanced/Basic provider note floated at the very bottom,
disconnected from the report.

## What changed

`save()` now returns structured `CacheReport` data instead of
pre-rendered HTML, and a single renderer (`caching-report.ts`) produces
one unified layout for all variants:

- **Section heading**: `#### <icon> Gradle Caching — <Provider>
(<status>)`
- **Status line** explaining what the cache did
- **Integrated provider note** woven in under the heading — now shown
**unconditionally** (no longer gated on license acceptance)
- **Expandable cache-entry details** when there are entries — basic
caching now gets this too

The two disabled variants (explicitly disabled, and skipped due to a
pre-existing Gradle User Home) render as **compact callouts with no
expandable section**.

### Main repo
- `caching-report.ts` (new): central renderer + all framing copy + entry
table/`<pre>` helpers.
- `cache-service.ts`: `CacheReport` / `CacheEntryReport` / status types;
`save()` returns `CacheReport`.
- `cache-service-loader.ts`: `NoOp` returns a report;
`LicenseWarningCacheService` removed; new `getProviderNote()`.
- `cache-service-basic.ts`: builds a `CacheReport`.
- `job-summary.ts` / `setup-gradle.ts`: thread `CacheReport` +
`ProviderNote`.
- `configuration.ts`: remove now-unused `isCacheLicenseAccepted()`.

### Vendored library
The structured contract requires **gradle-actions-caching v0.7.0**
(gradle/actions-caching#74). This PR updates the vendored library to
that release — the official `Update gradle-actions-caching library to
v0.7.0` vendor commit is included here, so merging this PR ships the
redesign together with the library it depends on.

## Testing

- Both repos build; prettier + eslint clean.
- `gradle/actions`: 363/363 Jest tests pass, including new
`caching-report.test.ts` covering every variant.
- `gradle-actions-caching`: 74/74 pass under JDK 17.
- Rendered markdown verified for all five variants (enhanced/basic
enabled & read-only, disabled, skipped).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Bot Githubaction <bot-githubaction@gradle.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 15:21:52 +00:00

148 lines
5.4 KiB
TypeScript

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 {CacheEntryReport, CacheOptions, CacheReport, CacheService} from './cache-service'
const ENTRY_NAME = 'Gradle User Home'
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<CacheReport> {
const primaryKey = core.getState(PRIMARY_KEY_STATE)
const restoredKey = core.getState(RESTORED_KEY_STATE)
if (cacheOptions.readOnly) {
return {
status: 'read-only',
entries: [
entryReport({
primaryKey,
restoredKey,
restoredOutcome: restoredKey
? '(Entry restored: exact match found)'
: '(Entry not restored: no match found)',
savedOutcome: '(Entry not saved: cache is read-only)'
})
]
}
}
if (restoredKey === primaryKey) {
core.info(`Basic caching restored entry with key \`${primaryKey}\`. Save was skipped.`)
return {
status: 'enabled',
entries: [
entryReport({
primaryKey,
restoredKey,
restoredOutcome: '(Entry restored: exact match found)',
savedOutcome: '(Entry not saved: entry with key already exists)'
})
]
}
}
const cachePaths = getCachePaths(gradleUserHome)
try {
await cache.saveCache(cachePaths, primaryKey)
core.info(`Basic caching saved entry with key: ${primaryKey}`)
return {
status: 'enabled',
entries: [
entryReport({
primaryKey,
restoredKey,
savedKey: primaryKey,
restoredOutcome: restoredKey
? '(Entry restored: exact match found)'
: '(Entry not restored: no match found)',
savedOutcome: '(Entry saved)'
})
]
}
} catch (error) {
core.warning(`Basic caching failed to save entry with key \`${primaryKey}\`: ${error}`)
return {
status: 'enabled',
entries: [
entryReport({
primaryKey,
restoredKey,
restoredOutcome: restoredKey
? '(Entry restored: exact match found)'
: '(Entry not restored: no match found)',
savedOutcome: `(Entry not saved: ${error})`
})
]
}
}
}
}
function entryReport(opts: {
primaryKey: string
restoredKey?: string
savedKey?: string
restoredOutcome: string
savedOutcome: string
}): CacheEntryReport {
return {
entryName: ENTRY_NAME,
requestedKey: opts.primaryKey || undefined,
restoredKey: opts.restoredKey || undefined,
restoredOutcome: opts.restoredOutcome,
savedKey: opts.savedKey || undefined,
savedOutcome: opts.savedOutcome
}
}
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}`
}