Skip to content

Commit 9eda6b5

Browse files
authored
feat: implement cache-dependency-path option to control caching dependency (actions#499)
1 parent 78078da commit 9eda6b5

File tree

14 files changed

+277
-58
lines changed

14 files changed

+277
-58
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
name: Validate cache with cache-dependency-path option
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- releases/*
8+
paths-ignore:
9+
- '**.md'
10+
pull_request:
11+
paths-ignore:
12+
- '**.md'
13+
14+
defaults:
15+
run:
16+
shell: bash
17+
18+
jobs:
19+
gradle1-save:
20+
runs-on: ${{ matrix.os }}
21+
strategy:
22+
fail-fast: false
23+
matrix:
24+
os: [macos-latest, windows-latest, ubuntu-latest]
25+
steps:
26+
- name: Checkout
27+
uses: actions/checkout@v3
28+
- name: Run setup-java with the cache for gradle
29+
uses: ./
30+
id: setup-java
31+
with:
32+
distribution: 'adopt'
33+
java-version: '11'
34+
cache: gradle
35+
cache-dependency-path: __tests__/cache/gradle1/*.gradle*
36+
- name: Create files to cache
37+
# Need to avoid using Gradle daemon to stabilize the save process on Windows
38+
# https://github.com/actions/cache/issues/454#issuecomment-840493935
39+
run: |
40+
gradle downloadDependencies --no-daemon -p __tests__/cache/gradle1
41+
if [ ! -d ~/.gradle/caches ]; then
42+
echo "::error::The ~/.gradle/caches directory does not exist unexpectedly"
43+
exit 1
44+
fi
45+
gradle1-restore:
46+
runs-on: ${{ matrix.os }}
47+
strategy:
48+
fail-fast: false
49+
matrix:
50+
os: [macos-latest, windows-latest, ubuntu-latest]
51+
needs: gradle1-save
52+
steps:
53+
- name: Checkout
54+
uses: actions/checkout@v3
55+
- name: Run setup-java with the cache for gradle
56+
uses: ./
57+
id: setup-java
58+
with:
59+
distribution: 'adopt'
60+
java-version: '11'
61+
cache: gradle
62+
cache-dependency-path: __tests__/cache/gradle1/*.gradle*
63+
- name: Confirm that ~/.gradle/caches directory has been made
64+
run: |
65+
if [ ! -d ~/.gradle/caches ]; then
66+
echo "::error::The ~/.gradle/caches directory does not exist unexpectedly"
67+
exit 1
68+
fi
69+
ls ~/.gradle/caches/
70+
gradle2-restore:
71+
runs-on: ${{ matrix.os }}
72+
strategy:
73+
fail-fast: false
74+
matrix:
75+
os: [macos-latest, windows-latest, ubuntu-latest]
76+
needs: gradle1-save
77+
steps:
78+
- name: Checkout
79+
uses: actions/checkout@v3
80+
- name: Run setup-java with the cache for gradle
81+
uses: ./
82+
id: setup-java
83+
with:
84+
distribution: 'adopt'
85+
java-version: '11'
86+
cache: gradle
87+
cache-dependency-path: __tests__/cache/gradle2/*.gradle*
88+
- name: Confirm that ~/.gradle/caches directory has not been made
89+
run: |
90+
if [ -d ~/.gradle/caches ]; then
91+
echo "::error::The ~/.gradle/caches directory exists unexpectedly"
92+
exit 1
93+
fi

.github/workflows/e2e-cache.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
# Need to avoid using Gradle daemon to stabilize the save process on Windows
3737
# https://github.com/actions/cache/issues/454#issuecomment-840493935
3838
run: |
39-
gradle downloadDependencies --no-daemon -p __tests__/cache/gradle
39+
gradle downloadDependencies --no-daemon -p __tests__/cache/gradle1
4040
if [ ! -d ~/.gradle/caches ]; then
4141
echo "::error::The ~/.gradle/caches directory does not exist unexpectedly"
4242
exit 1

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ This action allows you to work with Java and Scala projects.
4141

4242
- `cache`: Quick [setup caching](#caching-packages-dependencies) for the dependencies managed through one of the predefined package managers. It can be one of "maven", "gradle" or "sbt".
4343

44+
- `cache-dependency-path`: The path to a dependency file: pom.xml, build.gradle, build.sbt, etc. This option can be used with the `cache` option. If this option is omitted, the action searches for the dependency file in the entire repository. This option supports wildcards and a list of file names for caching multiple dependencies.
45+
4446
#### Maven options
4547
The action has a bunch of inputs to generate maven's [settings.xml](https://maven.apache.org/settings.html) on the fly and pass the values to Apache Maven GPG Plugin as well as Apache Maven Toolchains. See [advanced usage](docs/advanced-usage.md) for more.
4648

@@ -115,10 +117,13 @@ Currently, the following distributions are supported:
115117

116118
### Caching packages dependencies
117119
The action has a built-in functionality for caching and restoring dependencies. It uses [toolkit/cache](https://github.com/actions/toolkit/tree/main/packages/cache) under hood for caching dependencies but requires less configuration settings. Supported package managers are gradle, maven and sbt. The format of the used cache key is `setup-java-${{ platform }}-${{ packageManager }}-${{ fileHash }}`, where the hash is based on the following files:
120+
118121
- gradle: `**/*.gradle*`, `**/gradle-wrapper.properties`, `buildSrc/**/Versions.kt`, `buildSrc/**/Dependencies.kt`, `gradle/*.versions.toml`, and `**/versions.properties`
119122
- maven: `**/pom.xml`
120123
- sbt: all sbt build definition files `**/*.sbt`, `**/project/build.properties`, `**/project/**.scala`, `**/project/**.sbt`
121124

125+
When the option `cache-dependency-path` is specified, the hash is based on the matching file. This option supports wildcards and a list of file names, and is especially useful for monorepos.
126+
122127
The workflow output `cache-hit` is set to indicate if an exact match was found for the key [as actions/cache does](https://github.com/actions/cache/tree/main#outputs).
123128

124129
The cache input is optional, and caching is turned off by default.
@@ -132,6 +137,9 @@ steps:
132137
distribution: 'temurin'
133138
java-version: '17'
134139
cache: 'gradle'
140+
cache-dependency-path: | # optional
141+
sub-project/*.gradle*
142+
sub-project/**/gradle-wrapper.properties
135143
- run: ./gradlew build --no-daemon
136144
```
137145

@@ -144,6 +152,7 @@ steps:
144152
distribution: 'temurin'
145153
java-version: '17'
146154
cache: 'maven'
155+
cache-dependency-path: 'sub-project/pom.xml' # optional
147156
- name: Build with Maven
148157
run: mvn -B package --file pom.xml
149158
```
@@ -157,6 +166,9 @@ steps:
157166
distribution: 'temurin'
158167
java-version: '17'
159168
cache: 'sbt'
169+
cache-dependency-path: | # optional
170+
sub-project/build.sbt
171+
sub-project/project/build.properties
160172
- name: Build with SBT
161173
run: sbt package
162174
```

__tests__/cache.test.ts

Lines changed: 87 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as fs from 'fs';
66
import * as os from 'os';
77
import * as core from '@actions/core';
88
import * as cache from '@actions/cache';
9+
import * as glob from '@actions/glob';
910

1011
describe('dependency cache', () => {
1112
const ORIGINAL_RUNNER_OS = process.env['RUNNER_OS'];
@@ -64,25 +65,30 @@ describe('dependency cache', () => {
6465
ReturnType<typeof cache.restoreCache>,
6566
Parameters<typeof cache.restoreCache>
6667
>;
68+
let spyGlobHashFiles: jest.SpyInstance<
69+
ReturnType<typeof glob.hashFiles>,
70+
Parameters<typeof glob.hashFiles>
71+
>;
6772

6873
beforeEach(() => {
6974
spyCacheRestore = jest
7075
.spyOn(cache, 'restoreCache')
7176
.mockImplementation((paths: string[], primaryKey: string) =>
7277
Promise.resolve(undefined)
7378
);
79+
spyGlobHashFiles = jest.spyOn(glob, 'hashFiles');
7480
spyWarning.mockImplementation(() => null);
7581
});
7682

7783
it('throws error if unsupported package manager specified', () => {
78-
return expect(restore('ant')).rejects.toThrow(
84+
return expect(restore('ant', '')).rejects.toThrow(
7985
'unknown package manager specified: ant'
8086
);
8187
});
8288

8389
describe('for maven', () => {
8490
it('throws error if no pom.xml found', async () => {
85-
await expect(restore('maven')).rejects.toThrow(
91+
await expect(restore('maven', '')).rejects.toThrow(
8692
`No file in ${projectRoot(
8793
workspace
8894
)} matched to [**/pom.xml], make sure you have checked out the target repository`
@@ -91,15 +97,16 @@ describe('dependency cache', () => {
9197
it('downloads cache', async () => {
9298
createFile(join(workspace, 'pom.xml'));
9399

94-
await restore('maven');
100+
await restore('maven', '');
95101
expect(spyCacheRestore).toHaveBeenCalled();
102+
expect(spyGlobHashFiles).toHaveBeenCalledWith('**/pom.xml');
96103
expect(spyWarning).not.toHaveBeenCalled();
97104
expect(spyInfo).toHaveBeenCalledWith('maven cache is not found');
98105
});
99106
});
100107
describe('for gradle', () => {
101108
it('throws error if no build.gradle found', async () => {
102-
await expect(restore('gradle')).rejects.toThrow(
109+
await expect(restore('gradle', '')).rejects.toThrow(
103110
`No file in ${projectRoot(
104111
workspace
105112
)} matched to [**/*.gradle*,**/gradle-wrapper.properties,buildSrc/**/Versions.kt,buildSrc/**/Dependencies.kt,gradle/*.versions.toml,**/versions.properties], make sure you have checked out the target repository`
@@ -108,41 +115,53 @@ describe('dependency cache', () => {
108115
it('downloads cache based on build.gradle', async () => {
109116
createFile(join(workspace, 'build.gradle'));
110117

111-
await restore('gradle');
118+
await restore('gradle', '');
112119
expect(spyCacheRestore).toHaveBeenCalled();
120+
expect(spyGlobHashFiles).toHaveBeenCalledWith(
121+
'**/*.gradle*\n**/gradle-wrapper.properties\nbuildSrc/**/Versions.kt\nbuildSrc/**/Dependencies.kt\ngradle/*.versions.toml\n**/versions.properties'
122+
);
113123
expect(spyWarning).not.toHaveBeenCalled();
114124
expect(spyInfo).toHaveBeenCalledWith('gradle cache is not found');
115125
});
116126
it('downloads cache based on build.gradle.kts', async () => {
117127
createFile(join(workspace, 'build.gradle.kts'));
118128

119-
await restore('gradle');
129+
await restore('gradle', '');
120130
expect(spyCacheRestore).toHaveBeenCalled();
131+
expect(spyGlobHashFiles).toHaveBeenCalledWith(
132+
'**/*.gradle*\n**/gradle-wrapper.properties\nbuildSrc/**/Versions.kt\nbuildSrc/**/Dependencies.kt\ngradle/*.versions.toml\n**/versions.properties'
133+
);
121134
expect(spyWarning).not.toHaveBeenCalled();
122135
expect(spyInfo).toHaveBeenCalledWith('gradle cache is not found');
123136
});
124137
it('downloads cache based on libs.versions.toml', async () => {
125138
createDirectory(join(workspace, 'gradle'));
126139
createFile(join(workspace, 'gradle', 'libs.versions.toml'));
127140

128-
await restore('gradle');
141+
await restore('gradle', '');
129142
expect(spyCacheRestore).toHaveBeenCalled();
143+
expect(spyGlobHashFiles).toHaveBeenCalledWith(
144+
'**/*.gradle*\n**/gradle-wrapper.properties\nbuildSrc/**/Versions.kt\nbuildSrc/**/Dependencies.kt\ngradle/*.versions.toml\n**/versions.properties'
145+
);
130146
expect(spyWarning).not.toHaveBeenCalled();
131147
expect(spyInfo).toHaveBeenCalledWith('gradle cache is not found');
132148
});
133-
});
134-
it('downloads cache based on buildSrc/Versions.kt', async () => {
135-
createDirectory(join(workspace, 'buildSrc'));
136-
createFile(join(workspace, 'buildSrc', 'Versions.kt'));
149+
it('downloads cache based on buildSrc/Versions.kt', async () => {
150+
createDirectory(join(workspace, 'buildSrc'));
151+
createFile(join(workspace, 'buildSrc', 'Versions.kt'));
137152

138-
await restore('gradle');
139-
expect(spyCacheRestore).toHaveBeenCalled();
140-
expect(spyWarning).not.toHaveBeenCalled();
141-
expect(spyInfo).toHaveBeenCalledWith('gradle cache is not found');
153+
await restore('gradle', '');
154+
expect(spyCacheRestore).toHaveBeenCalled();
155+
expect(spyGlobHashFiles).toHaveBeenCalledWith(
156+
'**/*.gradle*\n**/gradle-wrapper.properties\nbuildSrc/**/Versions.kt\nbuildSrc/**/Dependencies.kt\ngradle/*.versions.toml\n**/versions.properties'
157+
);
158+
expect(spyWarning).not.toHaveBeenCalled();
159+
expect(spyInfo).toHaveBeenCalledWith('gradle cache is not found');
160+
});
142161
});
143162
describe('for sbt', () => {
144163
it('throws error if no build.sbt found', async () => {
145-
await expect(restore('sbt')).rejects.toThrow(
164+
await expect(restore('sbt', '')).rejects.toThrow(
146165
`No file in ${projectRoot(
147166
workspace
148167
)} matched to [**/*.sbt,**/project/build.properties,**/project/**.scala,**/project/**.sbt], make sure you have checked out the target repository`
@@ -151,8 +170,11 @@ describe('dependency cache', () => {
151170
it('downloads cache', async () => {
152171
createFile(join(workspace, 'build.sbt'));
153172

154-
await restore('sbt');
173+
await restore('sbt', '');
155174
expect(spyCacheRestore).toHaveBeenCalled();
175+
expect(spyGlobHashFiles).toHaveBeenCalledWith(
176+
'**/*.sbt\n**/project/build.properties\n**/project/**.scala\n**/project/**.sbt'
177+
);
156178
expect(spyWarning).not.toHaveBeenCalled();
157179
expect(spyInfo).toHaveBeenCalledWith('sbt cache is not found');
158180
});
@@ -161,19 +183,19 @@ describe('dependency cache', () => {
161183
createDirectory(join(workspace, 'project'));
162184
createFile(join(workspace, 'project/DependenciesV1.scala'));
163185

164-
await restore('sbt');
186+
await restore('sbt', '');
165187
const firstCall = spySaveState.mock.calls.toString();
166188

167189
spySaveState.mockClear();
168-
await restore('sbt');
190+
await restore('sbt', '');
169191
const secondCall = spySaveState.mock.calls.toString();
170192

171193
// Make sure multiple restores produce the same cache
172194
expect(firstCall).toBe(secondCall);
173195

174196
spySaveState.mockClear();
175197
createFile(join(workspace, 'project/DependenciesV2.scala'));
176-
await restore('sbt');
198+
await restore('sbt', '');
177199
const thirdCall = spySaveState.mock.calls.toString();
178200

179201
expect(firstCall).not.toBe(thirdCall);
@@ -182,11 +204,55 @@ describe('dependency cache', () => {
182204
it('downloads cache based on versions.properties', async () => {
183205
createFile(join(workspace, 'versions.properties'));
184206

185-
await restore('gradle');
207+
await restore('gradle', '');
186208
expect(spyCacheRestore).toHaveBeenCalled();
209+
expect(spyGlobHashFiles).toHaveBeenCalledWith(
210+
'**/*.gradle*\n**/gradle-wrapper.properties\nbuildSrc/**/Versions.kt\nbuildSrc/**/Dependencies.kt\ngradle/*.versions.toml\n**/versions.properties'
211+
);
187212
expect(spyWarning).not.toHaveBeenCalled();
188213
expect(spyInfo).toHaveBeenCalledWith('gradle cache is not found');
189214
});
215+
describe('cache-dependency-path', () => {
216+
it('throws error if no matching dependency file found', async () => {
217+
createFile(join(workspace, 'build.gradle.kts'));
218+
await expect(
219+
restore('gradle', 'sub-project/**/build.gradle.kts')
220+
).rejects.toThrow(
221+
`No file in ${projectRoot(
222+
workspace
223+
)} matched to [sub-project/**/build.gradle.kts], make sure you have checked out the target repository`
224+
);
225+
});
226+
it('downloads cache based on the specified pattern', async () => {
227+
createFile(join(workspace, 'build.gradle.kts'));
228+
createDirectory(join(workspace, 'sub-project1'));
229+
createFile(join(workspace, 'sub-project1', 'build.gradle.kts'));
230+
createDirectory(join(workspace, 'sub-project2'));
231+
createFile(join(workspace, 'sub-project2', 'build.gradle.kts'));
232+
233+
await restore('gradle', 'build.gradle.kts');
234+
expect(spyCacheRestore).toHaveBeenCalled();
235+
expect(spyGlobHashFiles).toHaveBeenCalledWith('build.gradle.kts');
236+
expect(spyWarning).not.toHaveBeenCalled();
237+
expect(spyInfo).toHaveBeenCalledWith('gradle cache is not found');
238+
239+
await restore('gradle', 'sub-project1/**/*.gradle*\n');
240+
expect(spyCacheRestore).toHaveBeenCalled();
241+
expect(spyGlobHashFiles).toHaveBeenCalledWith(
242+
'sub-project1/**/*.gradle*'
243+
);
244+
expect(spyWarning).not.toHaveBeenCalled();
245+
expect(spyInfo).toHaveBeenCalledWith('gradle cache is not found');
246+
247+
await restore('gradle', '*.gradle*\nsub-project2/**/*.gradle*\n');
248+
expect(spyCacheRestore).toHaveBeenCalled();
249+
expect(spyGlobHashFiles).toHaveBeenCalledWith(
250+
'*.gradle*\nsub-project2/**/*.gradle*'
251+
);
252+
expect(spyWarning).not.toHaveBeenCalled();
253+
expect(spyInfo).toHaveBeenCalledWith('gradle cache is not found');
254+
});
255+
});
190256
});
191257
describe('save', () => {
192258
let spyCacheSave: jest.SpyInstance<

__tests__/cache/gradle2/.gitignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.gradle
2+
**/build/
3+
!src/**/build/
4+
5+
# Ignore Gradle GUI config
6+
gradle-app.setting
7+
8+
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
9+
!gradle-wrapper.jar
10+
11+
# Cache of project
12+
.gradletasknamecache

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy