Cache Gradle wrapper distribution separately from the dependency cache (#1098)

The Gradle wrapper distribution (~/.gradle/wrapper) only depends on
gradle-wrapper.properties, which changes rarely, but it was previously
cached in the same entry as ~/.gradle/caches, keyed on volatile
**/*.gradle* files with no restoreKeys (issue #269). Every dependency
change therefore re-downloaded the wrapper.

Move ~/.gradle/wrapper into a dedicated `gradle-wrapper` additional
cache keyed only on **/gradle-wrapper.properties, reusing the
additionalCaches infrastructure introduced for the Maven wrapper fix.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Bruno Borges
2026-07-09 21:00:52 -04:00
committed by GitHub
parent bc6a9b1b01
commit ac3a4a8d4b
4 changed files with 117 additions and 12 deletions

View File

@@ -316,6 +316,43 @@ describe('dependency cache', () => {
expect(spyWarning).not.toHaveBeenCalled();
expect(spyInfo).toHaveBeenCalledWith('gradle cache is not found');
});
it('restores the gradle wrapper distribution cache independently of the main cache', async () => {
createFile(join(workspace, 'build.gradle'));
await restore('gradle', '');
// Main dependency cache no longer carries the wrapper path.
expect(spyCacheRestore).toHaveBeenCalledWith(
[join(os.homedir(), '.gradle', 'caches')],
expect.any(String)
);
// Wrapper distribution is restored on its own, keyed only on the
// wrapper properties file.
expect(spyCacheRestore).toHaveBeenCalledWith(
[join(os.homedir(), '.gradle', 'wrapper')],
expect.stringContaining('setup-java-')
);
expect(spyGlobHashFiles).toHaveBeenCalledWith(
'**/gradle-wrapper.properties'
);
});
it('skips the gradle wrapper cache when no wrapper properties exist', async () => {
createFile(join(workspace, 'build.gradle'));
spyGlobHashFiles.mockImplementation((pattern: string) =>
Promise.resolve(
pattern === '**/gradle-wrapper.properties' ? '' : 'hash-stub'
)
);
await restore('gradle', '');
// Only the main dependency cache is restored; the wrapper cache path is
// never touched because the project does not use the gradle wrapper.
expect(spyCacheRestore).toHaveBeenCalledTimes(1);
expect(spyCacheRestore).toHaveBeenCalledWith(
[join(os.homedir(), '.gradle', 'caches')],
expect.any(String)
);
expect(spyWarning).not.toHaveBeenCalled();
});
});
describe('for sbt', () => {
it('throws error if no build.sbt found', async () => {
@@ -587,6 +624,50 @@ describe('dependency cache', () => {
expect.stringMatching(/^Cache saved with the key:.*/)
);
});
it('saves the gradle wrapper distribution cache under its own key', async () => {
createFile(join(workspace, 'build.gradle'));
(core.getState as jest.Mock<any>).mockImplementation((name: any) => {
switch (name) {
case 'cache-primary-key':
return 'setup-java-cache-primary-key';
case 'cache-matched-key':
return 'setup-java-cache-matched-key';
case 'cache-primary-key-gradle-wrapper':
return 'setup-java-gradle-wrapper-key';
default:
return '';
}
});
await save('gradle');
expect(spyCacheSave).toHaveBeenCalledWith(
[join(os.homedir(), '.gradle', 'wrapper')],
'setup-java-gradle-wrapper-key'
);
expect(spyWarning).not.toHaveBeenCalled();
});
it('does not save the gradle wrapper cache on an exact wrapper hit', async () => {
createFile(join(workspace, 'build.gradle'));
(core.getState as jest.Mock<any>).mockImplementation((name: any) => {
switch (name) {
case 'cache-primary-key':
return 'setup-java-cache-primary-key';
case 'cache-matched-key':
return 'setup-java-cache-matched-key';
case 'cache-primary-key-gradle-wrapper':
case 'cache-matched-key-gradle-wrapper':
return 'setup-java-gradle-wrapper-key';
default:
return '';
}
});
await save('gradle');
expect(spyCacheSave).not.toHaveBeenCalledWith(
[join(os.homedir(), '.gradle', 'wrapper')],
expect.any(String)
);
});
});
describe('for sbt', () => {
it('uploads cache even if no build.sbt found', async () => {

16
dist/cleanup/index.js vendored
View File

@@ -99687,10 +99687,7 @@ const supportedPackageManager = [
},
{
id: 'gradle',
path: [
(0,external_path_.join)(external_os_default().homedir(), '.gradle', 'caches'),
(0,external_path_.join)(external_os_default().homedir(), '.gradle', 'wrapper')
],
path: [(0,external_path_.join)(external_os_default().homedir(), '.gradle', 'caches')],
// https://github.com/actions/cache/blob/0638051e9af2c23d10bb70fa9beffcad6cff9ce3/examples.md#java---gradle
pattern: [
'**/*.gradle*',
@@ -99699,6 +99696,17 @@ const supportedPackageManager = [
'buildSrc/**/Dependencies.kt',
'gradle/*.versions.toml',
'**/versions.properties'
],
// The Gradle wrapper distribution only depends on the wrapper properties,
// which change very rarely, so it is cached separately from the Gradle
// caches. This keeps it available across the frequent *.gradle* changes
// that rotate the main cache key. See issue #1095.
additionalCaches: [
{
name: 'gradle-wrapper',
path: [(0,external_path_.join)(external_os_default().homedir(), '.gradle', 'wrapper')],
pattern: ['**/gradle-wrapper.properties']
}
]
},
{

16
dist/setup/index.js vendored
View File

@@ -130909,10 +130909,7 @@ const supportedPackageManager = [
},
{
id: 'gradle',
path: [
(0,external_path_.join)(external_os_default().homedir(), '.gradle', 'caches'),
(0,external_path_.join)(external_os_default().homedir(), '.gradle', 'wrapper')
],
path: [(0,external_path_.join)(external_os_default().homedir(), '.gradle', 'caches')],
// https://github.com/actions/cache/blob/0638051e9af2c23d10bb70fa9beffcad6cff9ce3/examples.md#java---gradle
pattern: [
'**/*.gradle*',
@@ -130921,6 +130918,17 @@ const supportedPackageManager = [
'buildSrc/**/Dependencies.kt',
'gradle/*.versions.toml',
'**/versions.properties'
],
// The Gradle wrapper distribution only depends on the wrapper properties,
// which change very rarely, so it is cached separately from the Gradle
// caches. This keeps it available across the frequent *.gradle* changes
// that rotate the main cache key. See issue #1095.
additionalCaches: [
{
name: 'gradle-wrapper',
path: [(0,external_path_.join)(external_os_default().homedir(), '.gradle', 'wrapper')],
pattern: ['**/gradle-wrapper.properties']
}
]
},
{

View File

@@ -72,10 +72,7 @@ const supportedPackageManager: PackageManager[] = [
},
{
id: 'gradle',
path: [
join(os.homedir(), '.gradle', 'caches'),
join(os.homedir(), '.gradle', 'wrapper')
],
path: [join(os.homedir(), '.gradle', 'caches')],
// https://github.com/actions/cache/blob/0638051e9af2c23d10bb70fa9beffcad6cff9ce3/examples.md#java---gradle
pattern: [
'**/*.gradle*',
@@ -84,6 +81,17 @@ const supportedPackageManager: PackageManager[] = [
'buildSrc/**/Dependencies.kt',
'gradle/*.versions.toml',
'**/versions.properties'
],
// The Gradle wrapper distribution only depends on the wrapper properties,
// which change very rarely, so it is cached separately from the Gradle
// caches. This keeps it available across the frequent *.gradle* changes
// that rotate the main cache key. See issue #1095.
additionalCaches: [
{
name: 'gradle-wrapper',
path: [join(os.homedir(), '.gradle', 'wrapper')],
pattern: ['**/gradle-wrapper.properties']
}
]
},
{