mirror of
https://github.com/gradle/actions.git
synced 2026-06-16 16:40:39 +00:00
97715a29bc
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>
148 lines
5.4 KiB
TypeScript
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}`
|
|
}
|