Skip to content

Commit 18d114c

Browse files
committed
Add caching for the Mill build tool
1 parent f4f1212 commit 18d114c

File tree

9 files changed

+214
-7
lines changed

9 files changed

+214
-7
lines changed

.github/workflows/e2e-cache.yml

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,3 +207,93 @@ jobs:
207207
exit 1
208208
fi
209209
ls ~/.cache/coursier
210+
mill-save:
211+
runs-on: ${{ matrix.os }}
212+
defaults:
213+
run:
214+
shell: bash
215+
working-directory: __tests__/cache/mill
216+
strategy:
217+
fail-fast: false
218+
matrix:
219+
os: [macos-13, windows-latest, ubuntu-22.04]
220+
steps:
221+
- name: Checkout
222+
uses: actions/checkout@v4
223+
- name: Run setup-java with the cache for mill
224+
uses: ./
225+
id: setup-java
226+
with:
227+
distribution: 'adopt'
228+
java-version: '11'
229+
cache: mill
230+
- name: Create files to cache
231+
run: ./mill --disable-ticker _.compile
232+
233+
- name: Check files to cache on macos-latest
234+
if: matrix.os == 'macos-13'
235+
run: |
236+
if [ ! -d ~/.cache/mill/download ]; then
237+
echo "::error::The ~/.cache/mill/download directory does not exist unexpectedly"
238+
exit 1
239+
fi
240+
- name: Check files to cache on windows-latest
241+
if: matrix.os == 'windows-latest'
242+
run: |
243+
if [ ! -d %USERPROFILE%/.cache/mill/download ]; then
244+
echo "::error::The %USERPROFILE%/.cache/mill/download directory does not exist unexpectedly"
245+
exit 1
246+
fi
247+
- name: Check files to cache on ubuntu-latest
248+
if: matrix.os == 'ubuntu-latest'
249+
run: |
250+
if [ ! -d ~/.cache/mill/download ]; then
251+
echo "::error::The ~/.cache/mill/download directory does not exist unexpectedly"
252+
exit 1
253+
fi
254+
mill-restore:
255+
runs-on: ${{ matrix.os }}
256+
defaults:
257+
run:
258+
shell: bash
259+
working-directory: __tests__/cache/mill
260+
strategy:
261+
fail-fast: false
262+
matrix:
263+
os: [macos-13, windows-latest, ubuntu-22.04]
264+
needs: mill-save
265+
steps:
266+
- name: Checkout
267+
uses: actions/checkout@v4
268+
- name: Run setup-java with the cache for mill
269+
uses: ./
270+
id: setup-java
271+
with:
272+
distribution: 'adopt'
273+
java-version: '11'
274+
cache: mill
275+
276+
- name: Confirm that ~/.cache/mill/download directory has been made
277+
if: matrix.os == 'macos-13'
278+
run: |
279+
if [ ! -d ~/.cache/mill/download ]; then
280+
echo "::error::The ~/.cache/mill/download directory does not exist unexpectedly"
281+
exit 1
282+
fi
283+
ls ~/.cache/mill/download
284+
- name: Confirm that %USERPROFILE%/.cache/mill/download directory has been made
285+
if: matrix.os == 'windows-latest'
286+
run: |
287+
if [ ! -d %USERPROFILE%/.cache/mill/download ]; then
288+
echo "::error::The %USERPROFILE%/.cache/mill/download directory does not exist unexpectedly"
289+
exit 1
290+
fi
291+
ls %USERPROFILE%/.cache/mill/download
292+
- name: Confirm that ~/.cache/mill/download directory has been made
293+
if: matrix.os == 'ubuntu-latest'
294+
run: |
295+
if [ ! -d ~/.cache/mill/download ]; then
296+
echo "::error::The ~/.cache/mill/download directory does not exist unexpectedly"
297+
exit 1
298+
fi
299+
ls ~/.cache/mill/download

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# Ignore Scala IDE files
2+
.metals/
3+
14
# Ignore node_modules, ncc is used to compile nodejs modules into a single file
25
node_modules/
36
__tests__/runner/*

README.md

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ The `setup-java` action provides the following functionality for GitHub Actions
1414
- Caching dependencies managed by Apache Maven.
1515
- Caching dependencies managed by Gradle.
1616
- Caching dependencies managed by sbt.
17+
- Caching dependencies managed by Mill.
1718
- [Maven Toolchains declaration](https://maven.apache.org/guides/mini/guide-using-toolchains.html) for specified JDK versions.
1819

1920
This action allows you to work with Java and Scala projects.
@@ -39,7 +40,7 @@ This action allows you to work with Java and Scala projects.
3940

4041
- `check-latest`: Setting this option makes the action to check for the latest available version for the version spec.
4142

42-
- `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".
43+
- `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", "sbt", or "mill".
4344

4445
- `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.
4546

@@ -121,19 +122,20 @@ Currently, the following distributions are supported:
121122
**NOTE:** To comply with the GraalVM Free Terms and Conditions (GFTC) license, it is recommended to use GraalVM JDK 17 version 17.0.12, as this is the only version of GraalVM JDK 17 available under the GFTC license. Additionally, it is encouraged to consider upgrading to GraalVM JDK 21, which offers the latest features and improvements.
122123

123124
### Caching packages dependencies
124-
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:
125+
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, sbt, and Mill. The format of the used cache key is `setup-java-${{ platform }}-${{ packageManager }}-${{ fileHash }}`, where the hash is based on the following files:
125126

126-
- gradle: `**/*.gradle*`, `**/gradle-wrapper.properties`, `buildSrc/**/Versions.kt`, `buildSrc/**/Dependencies.kt`, `gradle/*.versions.toml`, and `**/versions.properties`
127-
- maven: `**/pom.xml`
127+
- Gradle: `**/*.gradle*`, `**/gradle-wrapper.properties`, `buildSrc/**/Versions.kt`, `buildSrc/**/Dependencies.kt`, `gradle/*.versions.toml`, and `**/versions.properties`
128+
- Maven: `**/pom.xml`
128129
- sbt: all sbt build definition files `**/*.sbt`, `**/project/build.properties`, `**/project/**.scala`, `**/project/**.sbt`
130+
- Mill: `**/build.sc`, `**/*.sc`, `**/mill`, `**/.mill-version`, and `**/.config/mill-version`
129131

130132
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.
131133

132134
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).
133135

134136
The cache input is optional, and caching is turned off by default.
135137

136-
#### Caching gradle dependencies
138+
#### Caching Gradle dependencies
137139
```yaml
138140
steps:
139141
- uses: actions/checkout@v4
@@ -148,7 +150,7 @@ steps:
148150
- run: ./gradlew build --no-daemon
149151
```
150152

151-
#### Caching maven dependencies
153+
#### Caching Maven dependencies
152154
```yaml
153155
steps:
154156
- uses: actions/checkout@v4
@@ -178,6 +180,21 @@ steps:
178180
run: sbt package
179181
```
180182

183+
#### Caching Mill dependencies
184+
```yaml
185+
steps:
186+
- uses: actions/checkout@v4
187+
- uses: actions/setup-java@v4
188+
with:
189+
distribution: 'temurin'
190+
java-version: '21'
191+
cache: 'mill'
192+
cache-dependency-path: | # optional
193+
sub-project/build.sc
194+
- name: Build with Mill
195+
run: ./mill _.compile
196+
```
197+
181198
#### Cache segment restore timeout
182199
Usually, cache gets downloaded in multiple segments of fixed sizes. Sometimes, a segment download gets stuck, which causes the workflow job to be stuck. The cache segment download timeout [was introduced](https://github.com/actions/toolkit/tree/main/packages/cache#cache-segment-restore-timeout) to solve this issue as it allows the segment download to get aborted and hence allows the job to proceed with a cache miss. The default value of the cache segment download timeout is set to 10 minutes and can be customized by specifying an environment variable named `SEGMENT_DOWNLOAD_TIMEOUT_MINS` with a timeout value in minutes.
183200

__tests__/cache.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,48 @@ describe('dependency cache', () => {
201201
expect(firstCall).not.toBe(thirdCall);
202202
});
203203
});
204+
describe('for mill', () => {
205+
it('throws error if no build.sc found', async () => {
206+
await expect(restore('mill', '')).rejects.toThrow(
207+
`No file in ${projectRoot(
208+
workspace
209+
)} matched to [**/build.sc,**/*.sc,**/mill,**/.mill-version,**/.config/mill-version], make sure you have checked out the target repository`
210+
);
211+
});
212+
it('downloads cache', async () => {
213+
createFile(join(workspace, 'build.sc'));
214+
215+
await restore('mill', '');
216+
expect(spyCacheRestore).toHaveBeenCalled();
217+
expect(spyGlobHashFiles).toHaveBeenCalledWith(
218+
'**/build.sc\n**/*.sc\n**/mill\n**/.mill-version\n**/.config/mill-version'
219+
);
220+
expect(spyWarning).not.toHaveBeenCalled();
221+
expect(spyInfo).toHaveBeenCalledWith('mill cache is not found');
222+
});
223+
it('detects scala and mill changes under **/mill-build/ folder', async () => {
224+
createFile(join(workspace, 'build.sc'));
225+
createDirectory(join(workspace, 'project'));
226+
createFile(join(workspace, '.config/mill-version'));
227+
228+
await restore('mill', '');
229+
const firstCall = spySaveState.mock.calls.toString();
230+
231+
spySaveState.mockClear();
232+
await restore('mill', '');
233+
const secondCall = spySaveState.mock.calls.toString();
234+
235+
// Make sure multiple restores produce the same cache
236+
expect(firstCall).toBe(secondCall);
237+
238+
spySaveState.mockClear();
239+
createFile(join(workspace, '.mill-version'));
240+
await restore('mill', '');
241+
const thirdCall = spySaveState.mock.calls.toString();
242+
243+
expect(firstCall).not.toBe(thirdCall);
244+
});
245+
});
204246
it('downloads cache based on versions.properties', async () => {
205247
createFile(join(workspace, 'versions.properties'));
206248

__tests__/cache/mill/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
out/

__tests__/cache/mill/.mill-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0.12.3

__tests__/cache/mill/build.sc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package build
2+
import mill._, scalalib._
3+
4+
object MyProject extends ScalaModule {
5+
def scalaVersion = "2.13.11"
6+
def ivyDeps = Agg(ivy"com.lihaoyi::mainargs:0.6.2")
7+
8+
object test extends ScalaTests {
9+
def ivyDeps = Agg(ivy"com.lihaoyi::utest:0.8.5")
10+
def testFramework = "utest.runner.Framework"
11+
}
12+
}

__tests__/cache/mill/mill

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/usr/bin/env sh
2+
# This is a wrapper script that automatically downloads Mill from GitHub.
3+
set -e
4+
5+
if [ -z "$MILL_VERSION" ] ; then
6+
MILL_VERSION="$(head -n 1 .mill-version 2> /dev/null)"
7+
fi
8+
9+
MILL_DOWNLOAD_PATH="$HOME/.cache/mill/download"
10+
MILL_EXEC_PATH="${MILL_DOWNLOAD_PATH}/$MILL_VERSION"
11+
12+
if [ ! -x "$MILL_EXEC_PATH" ] ; then
13+
mkdir -p "${MILL_DOWNLOAD_PATH}"
14+
DOWNLOAD_FILE=$MILL_EXEC_PATH-tmp-download
15+
MILL_DOWNLOAD_URL="https://github.com/lihaoyi/mill/releases/download/${MILL_VERSION%%-*}/$MILL_VERSION-assembly"
16+
curl --fail -L -o "$DOWNLOAD_FILE" "$MILL_DOWNLOAD_URL"
17+
chmod +x "$DOWNLOAD_FILE"
18+
mv "$DOWNLOAD_FILE" "$MILL_EXEC_PATH"
19+
unset DOWNLOAD_FILE
20+
unset MILL_DOWNLOAD_URL
21+
fi
22+
23+
unset MILL_DOWNLOAD_PATH
24+
unset MILL_VERSION
25+
26+
exec "${MILL_EXEC_PATH}" "$@"

src/cache.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const CACHE_MATCHED_KEY = 'cache-matched-key';
1313
const CACHE_KEY_PREFIX = 'setup-java';
1414

1515
interface PackageManager {
16-
id: 'maven' | 'gradle' | 'sbt';
16+
id: 'maven' | 'gradle' | 'sbt' | 'mill';
1717
/**
1818
* Paths of the file that specify the files to cache.
1919
*/
@@ -60,6 +60,21 @@ const supportedPackageManager: PackageManager[] = [
6060
'**/project/**.scala',
6161
'**/project/**.sbt'
6262
]
63+
},
64+
{
65+
id: 'mill',
66+
path: [
67+
join(os.homedir(), '.cache', 'mill')
68+
],
69+
pattern: [
70+
// https://github.com/coursier/cache-action/blob/4e2615869d13561d626ed48655e1a39e5b192b3c/README.md?plain=1#L28-L38
71+
'**/build.sc',
72+
'**/*.sc',
73+
'**/mill',
74+
'**/.mill-version',
75+
// https://github.com/com-lihaoyi/mill/blob/5b88d1e268e6264e44589c5ac82c0fdbd680fd63/mill#L6-L11
76+
'**/.config/mill-version'
77+
]
6378
}
6479
];
6580

0 commit comments

Comments
 (0)