mirror of
https://github.com/gradle/actions.git
synced 2026-07-03 09:20:51 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 448b3fda2e | |||
| d97d29c992 | |||
| 294a727064 | |||
| 1bbceb8c10 | |||
| 8a24b30373 | |||
| bc54cf57d1 |
+71
-71
File diff suppressed because one or more lines are too long
+2
-2
File diff suppressed because one or more lines are too long
+118
-118
File diff suppressed because one or more lines are too long
+3
-3
File diff suppressed because one or more lines are too long
Vendored
+70
-70
File diff suppressed because one or more lines are too long
Vendored
+2
-2
File diff suppressed because one or more lines are too long
Vendored
+138
-138
File diff suppressed because one or more lines are too long
Vendored
+3
-3
File diff suppressed because one or more lines are too long
@@ -32,9 +32,9 @@ export type CacheCleanupStatus =
|
|||||||
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)
|
||||||
| 'trial-expired' // past the hard trial expiry
|
| 'trial-expired' // past the hard trial expiry
|
||||||
| 'trial-not-licensed' // Develocity trial token missing or invalid
|
| 'not-registered' // neither entitlement path satisfied (no Develocity trial token, repo not registered)
|
||||||
| 'no-encryption-key' // Cannot store due to missing encryption key
|
| 'no-encryption-key' // Cannot store due to missing encryption key
|
||||||
| 'enabled' // Trial in effect: will attempt to save project state
|
| 'enabled' // Entitled: will attempt to save project state
|
||||||
|
|
||||||
export interface CacheEntryReport {
|
export interface CacheEntryReport {
|
||||||
entryName: string
|
entryName: string
|
||||||
|
|||||||
@@ -2,6 +2,21 @@ import {CacheCleanupStatus, CacheEntryReport, CacheReport, CacheStatus, ProjectC
|
|||||||
|
|
||||||
const DOCS = 'https://github.com/gradle/actions/blob/main/docs/setup-gradle.md'
|
const DOCS = 'https://github.com/gradle/actions/blob/main/docs/setup-gradle.md'
|
||||||
const DISTRIBUTION = 'https://github.com/gradle/actions/blob/main/DISTRIBUTION.md'
|
const DISTRIBUTION = 'https://github.com/gradle/actions/blob/main/DISTRIBUTION.md'
|
||||||
|
const REGISTER_BASE = 'https://actions-caching-registration.vercel.app/register'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The `/register` link with the current repo's identity appended, so the backend can resolve the
|
||||||
|
* installation and route to acceptance. Repo identity comes from `GITHUB_REPOSITORY` (`owner/repo`),
|
||||||
|
* set by the runner; falls back to the bare link if it is somehow unavailable.
|
||||||
|
*/
|
||||||
|
function registerUrl(): string {
|
||||||
|
const repository = process.env.GITHUB_REPOSITORY
|
||||||
|
if (!repository?.includes('/')) {
|
||||||
|
return REGISTER_BASE
|
||||||
|
}
|
||||||
|
const [owner, repo] = repository.split('/')
|
||||||
|
return `${REGISTER_BASE}?${new URLSearchParams({owner, repo}).toString()}`
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Identifies the caching provider in use, so the report can attribute the cache
|
* Identifies the caching provider in use, so the report can attribute the cache
|
||||||
@@ -28,10 +43,11 @@ const CLEANUP_COPY: Record<CacheCleanupStatus, string> = {
|
|||||||
'disabled-readonly': `[Cache cleanup](${DOCS}#configuring-cache-cleanup) is always disabled when the cache is read-only.`
|
'disabled-readonly': `[Cache cleanup](${DOCS}#configuring-cache-cleanup) is always disabled when the cache is read-only.`
|
||||||
}
|
}
|
||||||
|
|
||||||
const PROJECT_CACHE_COPY: Record<ProjectCacheStatus, string> = {
|
// 'not-registered' is rendered dynamically (see renderProjectCacheLine) because its /register link
|
||||||
|
// carries the repo identity, so it is intentionally absent from this static map.
|
||||||
|
const PROJECT_CACHE_COPY: Record<Exclude<ProjectCacheStatus, 'not-registered'>, string> = {
|
||||||
'not-enabled': ``,
|
'not-enabled': ``,
|
||||||
'trial-expired': `Project state (build-logic and configuration cache) was not cached - the Develocity caching trial has expired.`,
|
'trial-expired': `Project state (build-logic and configuration cache) was not cached - the Develocity caching trial has expired.`,
|
||||||
'trial-not-licensed': `Project state (build-logic and configuration cache) was not cached - a develocity-access-key and develocity-server-url is required.`,
|
|
||||||
'no-encryption-key': `Project state (build-logic and configuration cache) was not cached - a [cache-encryption-key](${DOCS}#cache-encryption-key) is required.`,
|
'no-encryption-key': `Project state (build-logic and configuration cache) was not cached - a [cache-encryption-key](${DOCS}#cache-encryption-key) is required.`,
|
||||||
enabled: `Caching of project state (build-logic and configuration cache) was enabled.`
|
enabled: `Caching of project state (build-logic and configuration cache) was enabled.`
|
||||||
}
|
}
|
||||||
@@ -78,8 +94,30 @@ function renderCleanupLine(cleanup?: CacheCleanupStatus): string | undefined {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderProjectCacheLine(projectCache?: ProjectCacheStatus): string | undefined {
|
function renderProjectCacheLine(projectCache?: ProjectCacheStatus): string | undefined {
|
||||||
|
if (!projectCache) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
if (projectCache === 'not-registered') {
|
||||||
|
return `Project state (build-logic and configuration cache) was not cached - this repository is not registered for advanced caching. [Register this repository](${registerUrl()}), or provide a \`develocity-access-key\` and \`develocity-server-url\`.`
|
||||||
|
}
|
||||||
// PROJECT_CACHE_COPY['not-enabled'] is '', which the .filter(Boolean) at the call site drops.
|
// PROJECT_CACHE_COPY['not-enabled'] is '', which the .filter(Boolean) at the call site drops.
|
||||||
return projectCache ? PROJECT_CACHE_COPY[projectCache] : undefined
|
return PROJECT_CACHE_COPY[projectCache]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A plain-text log notice (no markdown) surfaced when advanced caching was withheld because the
|
||||||
|
* repository is not registered. Returns `undefined` for every other status, so callers stay quiet
|
||||||
|
* when the feature is enabled, disabled, or simply not opted in.
|
||||||
|
*/
|
||||||
|
export function renderProjectCacheNotice(projectCache?: ProjectCacheStatus): string | undefined {
|
||||||
|
if (projectCache !== 'not-registered') {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
'Advanced caching (build-logic and configuration cache) was not enabled: this repository ' +
|
||||||
|
`is not registered. Register it at ${registerUrl()}, or provide a develocity-access-key and ` +
|
||||||
|
'develocity-server-url.'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderProviderNote(providerNote?: ProviderNote): string | undefined {
|
function renderProviderNote(providerNote?: ProviderNote): string | undefined {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {setupToken} from './develocity/short-lived-token'
|
|||||||
|
|
||||||
import {loadBuildResults, markBuildResultsProcessed} from './build-results'
|
import {loadBuildResults, markBuildResultsProcessed} from './build-results'
|
||||||
import {getCacheService, getProviderNote} from './cache-service-loader'
|
import {getCacheService, getProviderNote} from './cache-service-loader'
|
||||||
|
import {renderProjectCacheNotice} from './caching-report'
|
||||||
import {CacheOptions} from './cache-service'
|
import {CacheOptions} from './cache-service'
|
||||||
import {
|
import {
|
||||||
DevelocityConfig,
|
DevelocityConfig,
|
||||||
@@ -85,6 +86,14 @@ export async function complete(
|
|||||||
buildResults,
|
buildResults,
|
||||||
cacheOptionsFrom(cacheConfig, develocityServerUrl, cacheToken)
|
cacheOptionsFrom(cacheConfig, develocityServerUrl, cacheToken)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Surface a prominent notice (with the /register link) only when advanced caching was withheld
|
||||||
|
// because the repo is not registered; stay quiet when enabled by either path.
|
||||||
|
const registrationNotice = renderProjectCacheNotice(cacheReport.projectCache)
|
||||||
|
if (registrationNotice) {
|
||||||
|
core.notice(registrationNotice)
|
||||||
|
}
|
||||||
|
|
||||||
await jobSummary.generateJobSummary(buildResults, cacheReport, getProviderNote(cacheConfig), summaryConfig)
|
await jobSummary.generateJobSummary(buildResults, cacheReport, getProviderNote(cacheConfig), summaryConfig)
|
||||||
|
|
||||||
markBuildResultsProcessed()
|
markBuildResultsProcessed()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {describe, expect, it} from '@jest/globals'
|
import {describe, expect, it} from '@jest/globals'
|
||||||
|
|
||||||
import {CacheReport} from '../../src/cache-service'
|
import {CacheReport} from '../../src/cache-service'
|
||||||
import {renderCachingReport} from '../../src/caching-report'
|
import {renderCachingReport, renderProjectCacheNotice} from '../../src/caching-report'
|
||||||
|
|
||||||
const ENHANCED = {kind: 'enhanced'} as const
|
const ENHANCED = {kind: 'enhanced'} as const
|
||||||
const BASIC = {kind: 'basic'} as const
|
const BASIC = {kind: 'basic'} as const
|
||||||
@@ -114,6 +114,27 @@ describe('renderCachingReport', () => {
|
|||||||
expect(md).not.toContain('Project state')
|
expect(md).not.toContain('Project state')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('renders the not-registered status with a repo-scoped /register link inside the details', () => {
|
||||||
|
const saved = process.env.GITHUB_REPOSITORY
|
||||||
|
process.env.GITHUB_REPOSITORY = 'acme/widgets'
|
||||||
|
try {
|
||||||
|
const report: CacheReport = {
|
||||||
|
status: 'enabled',
|
||||||
|
cleanup: 'enabled',
|
||||||
|
projectCache: 'not-registered',
|
||||||
|
entries: [entry()]
|
||||||
|
}
|
||||||
|
const md = renderCachingReport(report, ENHANCED)
|
||||||
|
|
||||||
|
const detailsBody = md.slice(md.indexOf('</summary>'))
|
||||||
|
expect(detailsBody).toContain('not registered for advanced caching')
|
||||||
|
expect(detailsBody).toContain('/register?owner=acme&repo=widgets')
|
||||||
|
} finally {
|
||||||
|
if (saved === undefined) delete process.env.GITHUB_REPOSITORY
|
||||||
|
else process.env.GITHUB_REPOSITORY = saved
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
it('renders a compact disabled report with no note and no details', () => {
|
it('renders a compact disabled report with no note and no details', () => {
|
||||||
const report: CacheReport = {status: 'disabled', entries: []}
|
const report: CacheReport = {status: 'disabled', entries: []}
|
||||||
const md = renderCachingReport(report, undefined)
|
const md = renderCachingReport(report, undefined)
|
||||||
@@ -143,3 +164,27 @@ describe('renderCachingReport', () => {
|
|||||||
expect(md).not.toContain('<details>')
|
expect(md).not.toContain('<details>')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('renderProjectCacheNotice', () => {
|
||||||
|
it('returns a notice with a repo-scoped /register link for the not-registered status', () => {
|
||||||
|
const saved = process.env.GITHUB_REPOSITORY
|
||||||
|
process.env.GITHUB_REPOSITORY = 'acme/widgets'
|
||||||
|
try {
|
||||||
|
const notice = renderProjectCacheNotice('not-registered')
|
||||||
|
expect(notice).toBeDefined()
|
||||||
|
expect(notice).toContain('not registered')
|
||||||
|
expect(notice).toContain('/register?owner=acme&repo=widgets')
|
||||||
|
} finally {
|
||||||
|
if (saved === undefined) delete process.env.GITHUB_REPOSITORY
|
||||||
|
else process.env.GITHUB_REPOSITORY = saved
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('is silent for every other status', () => {
|
||||||
|
expect(renderProjectCacheNotice('enabled')).toBeUndefined()
|
||||||
|
expect(renderProjectCacheNotice('not-enabled')).toBeUndefined()
|
||||||
|
expect(renderProjectCacheNotice('trial-expired')).toBeUndefined()
|
||||||
|
expect(renderProjectCacheNotice('no-encryption-key')).toBeUndefined()
|
||||||
|
expect(renderProjectCacheNotice(undefined)).toBeUndefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
+3
-3
@@ -56,12 +56,12 @@ export declare type CacheStatus = 'enabled' | 'read-only' | 'write-only' | 'disa
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Status of project-entry caching (build-logic artifacts + configuration-cache data) for a run.
|
* Status of project-entry caching (build-logic artifacts + configuration-cache data) for a run.
|
||||||
* The first three are set on restore (always ungated); the rest are set on save and reflect the
|
* Set as the gate is evaluated: opt-in, then trial expiry, then entitlement (Develocity trial
|
||||||
* two-tier gate (opt-in + Develocity trial, then encryption key + Gradle version). Still beta.
|
* license OR repo registration), then encryption key. Still beta.
|
||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
declare type ProjectCacheStatus = 'not-enabled' | 'trial-expired' | 'trial-not-licensed' | 'no-encryption-key' | 'enabled';
|
declare type ProjectCacheStatus = 'not-enabled' | 'trial-expired' | 'not-registered' | 'no-encryption-key' | 'enabled';
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export declare function restore(gradleUserHome: string, cacheOptions: CacheOptions): Promise<void>;
|
export declare function restore(gradleUserHome: string, cacheOptions: CacheOptions): Promise<void>;
|
||||||
|
|||||||
+2
-2
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "gradle-actions-caching",
|
"name": "gradle-actions-caching",
|
||||||
"version": "0.9.0",
|
"version": "0.9.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./index.js",
|
"main": "./index.js",
|
||||||
"types": "./index.d.ts",
|
"types": "./index.d.ts",
|
||||||
|
|||||||
Reference in New Issue
Block a user