diff --git a/.github/workflows/e2e-cache.yml b/.github/workflows/e2e-cache.yml index c23c17780..74998dc02 100644 --- a/.github/workflows/e2e-cache.yml +++ b/.github/workflows/e2e-cache.yml @@ -111,3 +111,99 @@ jobs: exit 1 fi ls ~/.m2/repository + sbt-save: + runs-on: ${{ matrix.os }} + defaults: + run: + shell: bash + working-directory: __tests__/cache/sbt + strategy: + fail-fast: false + matrix: + os: [macos-latest, windows-latest, ubuntu-latest] + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Run setup-java with the cache for sbt + uses: ./ + id: setup-java + with: + distribution: 'adopt' + java-version: '11' + cache: sbt + - name: Create files to cache + run: sbt update + + - name: Check files to cache on macos-latest + if: matrix.os == 'macos-latest' + run: | + if [ ! -d ~/Library/Caches/Coursier ]; then + echo "::error::The ~/Library/Caches/Coursier directory does not exist unexpectedly" + exit 1 + fi + + - name: Check files to cache on windows-latest + if: matrix.os == 'windows-latest' + run: | + if [ ! -d ~/AppData/Local/Coursier/Cache ]; then + echo "::error::The ~/AppData/Local/Coursier/Cache directory does not exist unexpectedly" + exit 1 + fi + + + - name: Check files to cache on ubuntu-latest + if: matrix.os == 'ubuntu-latest' + run: | + if [ ! -d ~/.cache/coursier ]; then + echo "::error::The ~/.cache/coursier directory does not exist unexpectedly" + exit 1 + fi + + sbt-restore: + runs-on: ${{ matrix.os }} + defaults: + run: + shell: bash + working-directory: __tests__/cache/sbt + strategy: + fail-fast: false + matrix: + os: [macos-latest, windows-latest, ubuntu-latest] + needs: sbt-save + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Run setup-java with the cache for sbt + uses: ./ + id: setup-java + with: + distribution: 'adopt' + java-version: '11' + cache: sbt + + - name: Confirm that ~/Library/Caches/Coursier directory has been made + if: matrix.os == 'macos-latest' + run: | + if [ ! -d ~/Library/Caches/Coursier ]; then + echo "::error::The ~/Library/Caches/Coursier directory does not exist unexpectedly" + exit 1 + fi + ls ~/Library/Caches/Coursier + + - name: Confirm that ~/AppData/Local/Coursier/Cache directory has been made + if: matrix.os == 'windows-latest' + run: | + if [ ! -d ~/AppData/Local/Coursier/Cache ]; then + echo "::error::The ~/AppData/Local/Coursier/Cache directory does not exist unexpectedly" + exit 1 + fi + ls ~/AppData/Local/Coursier/Cache + + - name: Confirm that ~/.cache/coursier directory has been made + if: matrix.os == 'ubuntu-latest' + run: | + if [ ! -d ~/.cache/coursier ]; then + echo "::error::The ~/.cache/coursier directory does not exist unexpectedly" + exit 1 + fi + ls ~/.cache/coursier diff --git a/README.md b/README.md index 84a5e92c1..7b4245b02 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ Currently, the following distributions are supported: The action has a built-in functionality for caching and restoring dependencies. It uses [actions/cache](https://github.com/actions/cache) under hood for caching dependencies but requires less configuration settings. Supported package managers are gradle and maven. The format of the used cache key is `setup-java-${{ platform }}-${{ packageManager }}-${{ fileHash }}`, where the hash is based on the following files: - gradle: `**/*.gradle*`, `**/gradle-wrapper.properties` - maven: `**/pom.xml` +- sbt: `**/build.sbt` 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). @@ -99,6 +100,19 @@ steps: run: mvn -B package --file pom.xml ``` +#### Caching sbt dependencies +```yaml +steps: +- uses: actions/checkout@v3 +- uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '11' + cache: 'sbt' +- name: Build with SBT + run: sbt package +``` + ### Check latest In the basic examples above, the `check-latest` flag defaults to `false`. When set to `false`, the action tries to first resolve a version of Java from the local tool cache on the runner. If unable to find a specific version in the cache, the action will download a version of Java. Use the default or set `check-latest` to `false` if you prefer a faster more consistent setup experience that prioritizes trying to use the cached versions at the expense of newer versions sometimes being available for download. diff --git a/__tests__/cache.test.ts b/__tests__/cache.test.ts index b748e8027..2b42a5218 100644 --- a/__tests__/cache.test.ts +++ b/__tests__/cache.test.ts @@ -118,6 +118,23 @@ describe('dependency cache', () => { expect(spyInfo).toBeCalledWith('gradle cache is not found'); }); }); + describe('for sbt', () => { + it('throws error if no build.sbt found', async () => { + await expect(restore('sbt')).rejects.toThrowError( + `No file in ${projectRoot( + workspace + )} matched to [**/*.sbt,**/project/build.properties,**/project/**.{scala,sbt}], make sure you have checked out the target repository` + ); + }); + it('downloads cache', async () => { + createFile(join(workspace, 'build.sbt')); + + await restore('sbt'); + expect(spyCacheRestore).toBeCalled(); + expect(spyWarning).not.toBeCalled(); + expect(spyInfo).toBeCalledWith('sbt cache is not found'); + }); + }); }); describe('save', () => { let spyCacheSave: jest.SpyInstance< @@ -194,6 +211,30 @@ describe('dependency cache', () => { expect(spyInfo).toBeCalledWith(expect.stringMatching(/^Cache saved with the key:.*/)); }); }); + describe('for sbt', () => { + it('uploads cache even if no build.sbt found', async () => { + createStateForMissingBuildFile(); + await save('sbt'); + expect(spyCacheSave).toBeCalled(); + expect(spyWarning).not.toBeCalled(); + }); + it('does not upload cache if no restore run before', async () => { + createFile(join(workspace, 'build.sbt')); + + await save('sbt'); + expect(spyCacheSave).not.toBeCalled(); + expect(spyWarning).toBeCalledWith('Error retrieving key from state.'); + }); + it('uploads cache', async () => { + createFile(join(workspace, 'build.sbt')); + createStateForSuccessfulRestore(); + + await save('sbt'); + expect(spyCacheSave).toBeCalled(); + expect(spyWarning).not.toBeCalled(); + expect(spyInfo).toBeCalledWith(expect.stringMatching(/^Cache saved with the key:.*/)); + }); + }); }); }); diff --git a/__tests__/cache/sbt/.gitignore b/__tests__/cache/sbt/.gitignore new file mode 100644 index 000000000..9f970225a --- /dev/null +++ b/__tests__/cache/sbt/.gitignore @@ -0,0 +1 @@ +target/ \ No newline at end of file diff --git a/__tests__/cache/sbt/build.sbt b/__tests__/cache/sbt/build.sbt new file mode 100644 index 000000000..37a1513f0 --- /dev/null +++ b/__tests__/cache/sbt/build.sbt @@ -0,0 +1,3 @@ +ThisBuild / scalaVersion := "2.12.15" + +libraryDependencies += "org.scala-lang.modules" %% "scala-parser-combinators" % "1.1.2" \ No newline at end of file diff --git a/__tests__/cache/sbt/project/build.properties b/__tests__/cache/sbt/project/build.properties new file mode 100644 index 000000000..c8fcab543 --- /dev/null +++ b/__tests__/cache/sbt/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.6.2 diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index 3cc72cdc0..f2f91aec0 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -63300,8 +63300,24 @@ const supportedPackageManager = [ path: [path_1.join(os_1.default.homedir(), '.gradle', 'caches'), path_1.join(os_1.default.homedir(), '.gradle', 'wrapper')], // https://github.com/actions/cache/blob/0638051e9af2c23d10bb70fa9beffcad6cff9ce3/examples.md#java---gradle pattern: ['**/*.gradle*', '**/gradle-wrapper.properties'] + }, + { + id: 'sbt', + path: [ + path_1.join(os_1.default.homedir(), '.ivy2', 'cache'), + path_1.join(os_1.default.homedir(), '.sbt'), + getCoursierCachePath() + ], + pattern: ['**/*.sbt', '**/project/build.properties', '**/project/**.{scala,sbt}'] } ]; +function getCoursierCachePath() { + if (os_1.default.type() === 'Linux') + return path_1.join(os_1.default.homedir(), '.cache', 'coursier'); + if (os_1.default.type() === 'Darwin') + return path_1.join(os_1.default.homedir(), 'Library', 'Caches', 'Coursier'); + return path_1.join(os_1.default.homedir(), 'AppData', 'Local', 'Coursier', 'Cache'); +} function findPackageManager(id) { const packageManager = supportedPackageManager.find(packageManager => packageManager.id === id); if (packageManager === undefined) { diff --git a/dist/setup/index.js b/dist/setup/index.js index 826c05b56..f6516fe63 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -18632,8 +18632,24 @@ const supportedPackageManager = [ path: [path_1.join(os_1.default.homedir(), '.gradle', 'caches'), path_1.join(os_1.default.homedir(), '.gradle', 'wrapper')], // https://github.com/actions/cache/blob/0638051e9af2c23d10bb70fa9beffcad6cff9ce3/examples.md#java---gradle pattern: ['**/*.gradle*', '**/gradle-wrapper.properties'] + }, + { + id: 'sbt', + path: [ + path_1.join(os_1.default.homedir(), '.ivy2', 'cache'), + path_1.join(os_1.default.homedir(), '.sbt'), + getCoursierCachePath() + ], + pattern: ['**/*.sbt', '**/project/build.properties', '**/project/**.{scala,sbt}'] } ]; +function getCoursierCachePath() { + if (os_1.default.type() === 'Linux') + return path_1.join(os_1.default.homedir(), '.cache', 'coursier'); + if (os_1.default.type() === 'Darwin') + return path_1.join(os_1.default.homedir(), 'Library', 'Caches', 'Coursier'); + return path_1.join(os_1.default.homedir(), 'AppData', 'Local', 'Coursier', 'Cache'); +} function findPackageManager(id) { const packageManager = supportedPackageManager.find(packageManager => packageManager.id === id); if (packageManager === undefined) { diff --git a/src/cache.ts b/src/cache.ts index 3948a5a41..0ddc426de 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -13,7 +13,7 @@ const CACHE_MATCHED_KEY = 'cache-matched-key'; const CACHE_KEY_PREFIX = 'setup-java'; interface PackageManager { - id: 'maven' | 'gradle'; + id: 'maven' | 'gradle' | 'sbt'; /** * Paths of the file that specify the files to cache. */ @@ -32,9 +32,24 @@ const supportedPackageManager: PackageManager[] = [ path: [join(os.homedir(), '.gradle', 'caches'), join(os.homedir(), '.gradle', 'wrapper')], // https://github.com/actions/cache/blob/0638051e9af2c23d10bb70fa9beffcad6cff9ce3/examples.md#java---gradle pattern: ['**/*.gradle*', '**/gradle-wrapper.properties'] + }, + { + id: 'sbt', + path: [ + join(os.homedir(), '.ivy2', 'cache'), + join(os.homedir(), '.sbt'), + getCoursierCachePath() + ], + pattern: ['**/*.sbt', '**/project/build.properties', '**/project/**.{scala,sbt}'] } ]; +function getCoursierCachePath(): string { + if (os.type() === 'Linux') return join(os.homedir(), '.cache', 'coursier'); + if (os.type() === 'Darwin') return join(os.homedir(), 'Library', 'Caches', 'Coursier'); + return join(os.homedir(), 'AppData', 'Local', 'Coursier', 'Cache'); +} + function findPackageManager(id: string): PackageManager { const packageManager = supportedPackageManager.find(packageManager => packageManager.id === id); if (packageManager === undefined) {