From 0b56831a101f17def6accc67364f79eafa26b409 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Wed, 29 Jul 2026 14:43:55 -0400 Subject: [PATCH] Add dependency cache path overrides (#1175) * Add dependency cache path overrides Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: dd650d36-9c97-4ca4-9ec8-39b37f99a07c * Clarify supported dependency cache managers Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: dd650d36-9c97-4ca4-9ec8-39b37f99a07c * Fix custom cache path CI checks Align the custom cache save and restore key inputs and use the workflow hash to avoid a previously populated cache entry. Rebuild the distribution bundles. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: dd650d36-9c97-4ca4-9ec8-39b37f99a07c --------- Copilot-Session: dd650d36-9c97-4ca4-9ec8-39b37f99a07c --- .github/workflows/e2e-cache.yml | 51 ++++++++++++++++++++++- README.md | 30 ++++++++++++++ __tests__/cache.test.ts | 72 +++++++++++++++++++++++++++++++-- __tests__/setup-java.test.ts | 9 ++++- action.yml | 3 ++ dist/cleanup/index.js | 33 ++++++++++++--- dist/setup/index.js | 36 +++++++++++++---- src/cache.ts | 43 +++++++++++++++++--- src/constants.ts | 1 + src/setup-java.ts | 3 +- 10 files changed, 256 insertions(+), 25 deletions(-) diff --git a/.github/workflows/e2e-cache.yml b/.github/workflows/e2e-cache.yml index 2bab56dc..cfb254ec 100644 --- a/.github/workflows/e2e-cache.yml +++ b/.github/workflows/e2e-cache.yml @@ -335,7 +335,9 @@ jobs: distribution: 'adopt' java-version: '11' cache: maven - cache-dependency-path: __tests__/cache/maven2/pom.xml + cache-dependency-path: | + __tests__/cache/maven2/pom.xml + README.md - name: Confirm that ~/.m2/repository directory has not been made run: bash __tests__/check-dir.sh "$HOME/.m2/repository" absent sbt1-save: @@ -446,3 +448,50 @@ jobs: - name: Confirm that ~/.cache/coursier directory has not been made if: matrix.os == 'ubuntu-22.04' run: bash "$GITHUB_WORKSPACE/__tests__/check-dir.sh" "$HOME/.cache/coursier" absent + custom-maven-path-save: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v7 + with: + persist-credentials: false + - name: Run setup-java with a custom Maven cache path + uses: ./ + with: + distribution: 'adopt' + java-version: '11' + cache: maven + cache-dependency-path: | + __tests__/cache/maven2/pom.xml + .github/workflows/e2e-cache.yml + cache-path: | + ${{ runner.temp }}/setup-java-custom-maven-repository + !${{ runner.temp }}/setup-java-custom-maven-repository/**/*.lastUpdated + - name: Populate the custom Maven repository + run: | + mvn -Dmaven.repo.local="$RUNNER_TEMP/setup-java-custom-maven-repository" verify -f __tests__/cache/maven2/pom.xml + touch "$RUNNER_TEMP/setup-java-custom-maven-repository/setup-java-cache-path-marker" + bash __tests__/check-dir.sh "$RUNNER_TEMP/setup-java-custom-maven-repository" + custom-maven-path-restore: + runs-on: ubuntu-latest + needs: custom-maven-path-save + steps: + - name: Checkout + uses: actions/checkout@v7 + with: + persist-credentials: false + - name: Run setup-java with a custom Maven cache path + uses: ./ + with: + distribution: 'adopt' + java-version: '11' + cache: maven + cache-dependency-path: | + __tests__/cache/maven2/pom.xml + .github/workflows/e2e-cache.yml + cache-path: | + ${{ runner.temp }}/setup-java-custom-maven-repository + !${{ runner.temp }}/setup-java-custom-maven-repository/**/*.lastUpdated + cache-read-only: true + - name: Confirm that the custom Maven repository has been restored + run: test -f "$RUNNER_TEMP/setup-java-custom-maven-repository/setup-java-cache-path-marker" diff --git a/README.md b/README.md index 38ecc35f..38d93876 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,8 @@ For more details, see the full release notes on the [releases page](https://git - `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. + - `cache-path`: The dependency cache path to use instead of the default path for the package manager selected by `cache`. This option supports a list of paths and exclusion patterns. The build tool must be configured to use the same location. + - `cache-read-only`: Restore dependency caches without saving changes in the post action. Defaults to `false`. Use this for pull requests, merge queues, short-lived branches, and fan-out jobs that should consume caches populated by a default-branch or seed job. #### Maven options @@ -188,6 +190,34 @@ The action has a built-in functionality for caching and restoring dependencies. 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. +Use `cache-path` to replace the selected package manager's default dependency +cache paths. Each non-empty line is passed to `actions/cache`, including +supported exclusion patterns. `setup-java` does not configure the build tool, +so the build must use the same paths: + +```yaml +- uses: actions/setup-java@v6 + with: + distribution: 'temurin' + java-version: '25' + cache: 'maven' + cache-path: | + /custom/maven/repository + !/custom/maven/repository/**/*.lastUpdated +- run: mvn -Dmaven.repo.local=/custom/maven/repository verify +``` + +`cache-path` does not change the cache key. The key continues to use the runner +OS, architecture, selected package manager, and dependency-file hash described +above. Jobs intended to share a cache key must therefore use the same +`cache-path` values so that they restore and save the same filesystem +locations. + +The Maven and Gradle wrapper caches remain at their documented default paths +and are managed independently of `cache-path`. For advanced keying, fallback +keys, or cache topologies that do not map to one package manager's dependency +paths, use [`actions/cache`](https://github.com/actions/cache) directly. + 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). The workflow output `cache-primary-key` exposes the primary cache key computed by the action for the configured build tool. It is useful for composing with [`actions/cache`](https://github.com/actions/cache) or [`actions/cache/restore`](https://github.com/actions/cache/tree/main/restore) in later steps or dependent jobs that need to reuse the exact same key. It is empty when caching is not enabled or when caching is skipped (for example, when the cache service is unavailable). diff --git a/__tests__/cache.test.ts b/__tests__/cache.test.ts index 5957f2df..9fa6a90b 100644 --- a/__tests__/cache.test.ts +++ b/__tests__/cache.test.ts @@ -224,10 +224,10 @@ describe('dependency cache', () => { join(workspace, '.mvn', 'wrapper', 'maven-wrapper.properties') ); - await restore('maven', ''); + await restore('maven', '', ['/custom/maven/repository']); // Main dependency cache no longer carries the wrapper dists path. expect(spyCacheRestore).toHaveBeenCalledWith( - [join(os.homedir(), '.m2', 'repository')], + ['/custom/maven/repository'], expect.any(String) ); expect(spyCacheRestore).toHaveBeenCalledWith( @@ -394,10 +394,10 @@ describe('dependency cache', () => { it('restores the gradle wrapper distribution cache independently of the main cache', async () => { createFile(join(workspace, 'build.gradle')); - await restore('gradle', ''); + await restore('gradle', '', ['/custom/gradle/caches']); // Main dependency cache no longer carries the wrapper path. expect(spyCacheRestore).toHaveBeenCalledWith( - [join(os.homedir(), '.gradle', 'caches')], + ['/custom/gradle/caches'], expect.any(String) ); // Wrapper distribution is restored on its own, keyed only on the @@ -576,6 +576,34 @@ describe('dependency cache', () => { expect(spyInfo).toHaveBeenCalledWith('gradle cache is not found'); }); }); + describe('cache-path', () => { + it.each([ + ['maven', ['/custom/maven/repository']], + ['gradle', ['/custom/gradle/caches']], + [ + 'sbt', + [ + '/custom/ivy/cache', + '/custom/coursier/cache', + '!/custom/ivy/cache/*.lock' + ] + ] + ])( + 'restores and persists custom paths for %s', + async (packageManager, cachePaths) => { + await restore(packageManager, '', cachePaths); + + expect(spyCacheRestore).toHaveBeenCalledWith( + cachePaths, + expect.any(String) + ); + expect(spySaveState).toHaveBeenCalledWith( + 'cache-paths', + JSON.stringify(cachePaths) + ); + } + ); + }); }); describe('save', () => { let spyCacheSave: any; @@ -625,6 +653,42 @@ describe('dependency cache', () => { ); }); + it.each([ + ['maven', ['/custom/maven/repository']], + ['gradle', ['/custom/gradle/caches']], + [ + 'sbt', + [ + '/custom/ivy/cache', + '/custom/coursier/cache', + '!/custom/ivy/cache/*.lock' + ] + ] + ])( + 'saves the persisted custom paths for %s', + async (packageManager, cachePaths) => { + (core.getState as jest.Mock).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-paths': + return JSON.stringify(cachePaths); + default: + return ''; + } + }); + + await save(packageManager); + + expect(spyCacheSave).toHaveBeenCalledWith( + cachePaths, + 'setup-java-cache-primary-key' + ); + } + ); + describe('for maven', () => { it('uploads cache even if no pom.xml found', async () => { createStateForMissingBuildFile(); diff --git a/__tests__/setup-java.test.ts b/__tests__/setup-java.test.ts index 58785ccc..dd92872f 100644 --- a/__tests__/setup-java.test.ts +++ b/__tests__/setup-java.test.ts @@ -301,6 +301,10 @@ describe('setup action orchestration', () => { inputs.set('cache', 'maven'); inputs.set('cache-dependency-path', '**/pom.xml'); multilineInputs.set('java-version', ['21']); + multilineInputs.set('cache-path', [ + '/custom/maven/repository', + '!/custom/maven/repository/excluded' + ]); const setupJava = jest.fn(async () => ({ version: '21.0.4+7', path: '/opt/java/21' @@ -312,7 +316,10 @@ describe('setup action orchestration', () => { expect(problemMatcher.configureProblemMatcher).toHaveBeenCalledWith( expect.stringMatching(/\.github[/\\]java\.json$/) ); - expect(cache.restore).toHaveBeenCalledWith('maven', '**/pom.xml'); + expect(cache.restore).toHaveBeenCalledWith('maven', '**/pom.xml', [ + '/custom/maven/repository', + '!/custom/maven/repository/excluded' + ]); expect( (toolchains.configureToolchains as jest.Mock).mock.invocationCallOrder[0] ).toBeLessThan( diff --git a/action.yml b/action.yml index 78ccfbce..2b4072ea 100644 --- a/action.yml +++ b/action.yml @@ -87,6 +87,9 @@ inputs: cache-dependency-path: description: '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.' required: false + cache-path: + description: 'The path to cache instead of the default dependency cache path for the selected package manager. This option can be used with the `cache` option and supports a list of paths and exclusion patterns.' + required: false cache-read-only: description: 'Restore dependency caches without saving cache changes in the post action.' required: false diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index a78aa719..3d6bd159 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -97338,6 +97338,7 @@ const MAVEN_GPG_PASSPHRASE_DEFAULT_ENV = 'MAVEN_GPG_PASSPHRASE'; const GPG_PASSPHRASE_PROFILE_ID = 'setup-java-gpg'; const INPUT_CACHE = 'cache'; const INPUT_CACHE_DEPENDENCY_PATH = 'cache-dependency-path'; +const INPUT_CACHE_PATH = 'cache-path'; const INPUT_CACHE_READ_ONLY = 'cache-read-only'; const INPUT_JOB_STATUS = 'job-status'; const STATE_GPG_PRIVATE_KEY_FINGERPRINT = 'gpg-private-key-fingerprint'; @@ -97771,6 +97772,7 @@ async function verifyPackageSignature(archivePath, signatureUrl, publicKeyConten const STATE_CACHE_PRIMARY_KEY = 'cache-primary-key'; +const STATE_CACHE_PATHS = 'cache-paths'; const CACHE_MATCHED_KEY = 'cache-matched-key'; const CACHE_KEY_PREFIX = 'setup-java'; const supportedPackageManager = [ @@ -97852,6 +97854,21 @@ function findPackageManager(id) { } return packageManager; } +function resolveCachePaths(packageManager, cachePaths) { + return cachePaths.length > 0 ? cachePaths : packageManager.path; +} +function getCachePathsFromState(packageManager) { + const cachePathsState = getState(STATE_CACHE_PATHS); + if (!cachePathsState) { + return packageManager.path; + } + const cachePaths = JSON.parse(cachePathsState); + if (!Array.isArray(cachePaths) || + !cachePaths.every(cachePath => typeof cachePath === 'string')) { + throw new Error('Invalid cache paths retrieved from state.'); + } + return cachePaths; +} /** * State keys used to carry an additional cache's restore-time information over * to the post (save) action, scoped by the additional cache name. @@ -97894,30 +97911,33 @@ async function computeAdditionalCacheKey(additionalCache) { } /** * Restore the dependency cache - * @param id ID of the package manager, should be "maven" or "gradle" + * @param id ID of the package manager, should be "maven", "gradle", or "sbt" * @param cacheDependencyPath The path to a dependency file + * @param cachePaths Paths to cache instead of the package manager defaults */ -async function restore(id, cacheDependencyPath) { +async function restore(id, cacheDependencyPath, cachePaths = []) { const packageManager = findPackageManager(id); + const resolvedCachePaths = resolveCachePaths(packageManager, cachePaths); const [primaryKey, preparedAdditionalCaches] = await Promise.all([ computeCacheKey(packageManager, cacheDependencyPath), prepareAdditionalCaches(packageManager.additionalCaches ?? []) ]); core.debug(`primary key is ${primaryKey}`); core.saveState(STATE_CACHE_PRIMARY_KEY, primaryKey); + core.saveState(STATE_CACHE_PATHS, JSON.stringify(resolvedCachePaths)); core.setOutput(STATE_CACHE_PRIMARY_KEY, primaryKey); for (const preparedCache of preparedAdditionalCaches) { core.debug(`${preparedCache.cache.name} primary key is ${preparedCache.primaryKey}`); core.saveState(additionalCachePrimaryKeyState(preparedCache.cache.name), preparedCache.primaryKey); } await Promise.all([ - restorePrimaryCache(packageManager, primaryKey), + restorePrimaryCache(packageManager, resolvedCachePaths, primaryKey), ...preparedAdditionalCaches.map(preparedCache => restoreAdditionalCache(preparedCache)) ]); } -async function restorePrimaryCache(packageManager, primaryKey) { +async function restorePrimaryCache(packageManager, cachePaths, primaryKey) { // No "restoreKeys" is set, to start with a clear cache after dependency update (see https://github.com/actions/setup-java/issues/269) - const matchedKey = await cache.restoreCache(packageManager.path, primaryKey); + const matchedKey = await cache.restoreCache(cachePaths, primaryKey); if (matchedKey) { core.saveState(CACHE_MATCHED_KEY, matchedKey); core.setOutput('cache-hit', matchedKey === primaryKey); @@ -97963,6 +97983,7 @@ async function restoreAdditionalCache(preparedCache) { */ async function save(id) { const packageManager = findPackageManager(id); + const cachePaths = getCachePathsFromState(packageManager); const matchedKey = getState(CACHE_MATCHED_KEY); // Inputs are re-evaluated before the post action, so we want the original key used for restore const primaryKey = getState(STATE_CACHE_PRIMARY_KEY); @@ -97985,7 +98006,7 @@ async function save(id) { return; } try { - const cacheId = await cache_saveCache(packageManager.path, primaryKey); + const cacheId = await cache_saveCache(cachePaths, primaryKey); if (cacheId === -1) { // saveCache returns -1 without throwing when the cache was not saved, // e.g. a reserve collision or a read-only token (fork PR). @actions/cache diff --git a/dist/setup/index.js b/dist/setup/index.js index 5830f946..6e88b9c6 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -72120,6 +72120,7 @@ const MAVEN_GPG_PASSPHRASE_DEFAULT_ENV = 'MAVEN_GPG_PASSPHRASE'; const GPG_PASSPHRASE_PROFILE_ID = 'setup-java-gpg'; const INPUT_CACHE = 'cache'; const INPUT_CACHE_DEPENDENCY_PATH = 'cache-dependency-path'; +const INPUT_CACHE_PATH = 'cache-path'; const INPUT_CACHE_READ_ONLY = 'cache-read-only'; const constants_INPUT_JOB_STATUS = 'job-status'; const STATE_GPG_PRIVATE_KEY_FINGERPRINT = 'gpg-private-key-fingerprint'; @@ -129026,6 +129027,7 @@ async function writeToolchainsFileToDisk(directory, settings) { const STATE_CACHE_PRIMARY_KEY = 'cache-primary-key'; +const STATE_CACHE_PATHS = 'cache-paths'; const CACHE_MATCHED_KEY = 'cache-matched-key'; const CACHE_KEY_PREFIX = 'setup-java'; const supportedPackageManager = [ @@ -129107,6 +129109,21 @@ function findPackageManager(id) { } return packageManager; } +function resolveCachePaths(packageManager, cachePaths) { + return cachePaths.length > 0 ? cachePaths : packageManager.path; +} +function getCachePathsFromState(packageManager) { + const cachePathsState = core.getState(STATE_CACHE_PATHS); + if (!cachePathsState) { + return packageManager.path; + } + const cachePaths = JSON.parse(cachePathsState); + if (!Array.isArray(cachePaths) || + !cachePaths.every(cachePath => typeof cachePath === 'string')) { + throw new Error('Invalid cache paths retrieved from state.'); + } + return cachePaths; +} /** * State keys used to carry an additional cache's restore-time information over * to the post (save) action, scoped by the additional cache name. @@ -129149,30 +129166,33 @@ async function computeAdditionalCacheKey(additionalCache) { } /** * Restore the dependency cache - * @param id ID of the package manager, should be "maven" or "gradle" + * @param id ID of the package manager, should be "maven", "gradle", or "sbt" * @param cacheDependencyPath The path to a dependency file + * @param cachePaths Paths to cache instead of the package manager defaults */ -async function restore(id, cacheDependencyPath) { +async function restore(id, cacheDependencyPath, cachePaths = []) { const packageManager = findPackageManager(id); + const resolvedCachePaths = resolveCachePaths(packageManager, cachePaths); const [primaryKey, preparedAdditionalCaches] = await Promise.all([ computeCacheKey(packageManager, cacheDependencyPath), prepareAdditionalCaches(packageManager.additionalCaches ?? []) ]); core_debug(`primary key is ${primaryKey}`); saveState(STATE_CACHE_PRIMARY_KEY, primaryKey); + saveState(STATE_CACHE_PATHS, JSON.stringify(resolvedCachePaths)); setOutput(STATE_CACHE_PRIMARY_KEY, primaryKey); for (const preparedCache of preparedAdditionalCaches) { core_debug(`${preparedCache.cache.name} primary key is ${preparedCache.primaryKey}`); saveState(additionalCachePrimaryKeyState(preparedCache.cache.name), preparedCache.primaryKey); } await Promise.all([ - restorePrimaryCache(packageManager, primaryKey), + restorePrimaryCache(packageManager, resolvedCachePaths, primaryKey), ...preparedAdditionalCaches.map(preparedCache => restoreAdditionalCache(preparedCache)) ]); } -async function restorePrimaryCache(packageManager, primaryKey) { +async function restorePrimaryCache(packageManager, cachePaths, primaryKey) { // No "restoreKeys" is set, to start with a clear cache after dependency update (see https://github.com/actions/setup-java/issues/269) - const matchedKey = await restoreCache(packageManager.path, primaryKey); + const matchedKey = await restoreCache(cachePaths, primaryKey); if (matchedKey) { saveState(CACHE_MATCHED_KEY, matchedKey); setOutput('cache-hit', matchedKey === primaryKey); @@ -129218,6 +129238,7 @@ async function restoreAdditionalCache(preparedCache) { */ async function save(id) { const packageManager = findPackageManager(id); + const cachePaths = getCachePathsFromState(packageManager); const matchedKey = core.getState(CACHE_MATCHED_KEY); // Inputs are re-evaluated before the post action, so we want the original key used for restore const primaryKey = core.getState(STATE_CACHE_PRIMARY_KEY); @@ -129240,7 +129261,7 @@ async function save(id) { return; } try { - const cacheId = await cache.saveCache(packageManager.path, primaryKey); + const cacheId = await cache.saveCache(cachePaths, primaryKey); if (cacheId === -1) { // saveCache returns -1 without throwing when the cache was not saved, // e.g. a reserve collision or a read-only token (fork PR). @actions/cache @@ -132608,6 +132629,7 @@ async function run() { const jdkFile = getJdkFileInput(); const cache = getInput(INPUT_CACHE); const cacheDependencyPath = getInput(INPUT_CACHE_DEPENDENCY_PATH); + const cachePath = getMultilineInput(INPUT_CACHE_PATH); const checkLatest = util_getBooleanInput(INPUT_CHECK_LATEST, false); const forceDownload = util_getBooleanInput(INPUT_FORCE_DOWNLOAD, false); const setDefault = util_getBooleanInput(INPUT_SET_DEFAULT, true); @@ -132676,7 +132698,7 @@ async function run() { await configureAuthentication(); configureMavenArgs(); if (cache && isCacheFeatureAvailable()) { - await restore(cache, cacheDependencyPath); + await restore(cache, cacheDependencyPath, cachePath); } } catch (error) { diff --git a/src/cache.ts b/src/cache.ts index c5d0e0e4..27ad4d5b 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -9,6 +9,7 @@ import * as core from '@actions/core'; import * as glob from '@actions/glob'; const STATE_CACHE_PRIMARY_KEY = 'cache-primary-key'; +const STATE_CACHE_PATHS = 'cache-paths'; const CACHE_MATCHED_KEY = 'cache-matched-key'; const CACHE_KEY_PREFIX = 'setup-java'; @@ -136,6 +137,29 @@ function findPackageManager(id: string): PackageManager { return packageManager; } +function resolveCachePaths( + packageManager: PackageManager, + cachePaths: string[] +): string[] { + return cachePaths.length > 0 ? cachePaths : packageManager.path; +} + +function getCachePathsFromState(packageManager: PackageManager): string[] { + const cachePathsState = core.getState(STATE_CACHE_PATHS); + if (!cachePathsState) { + return packageManager.path; + } + + const cachePaths: unknown = JSON.parse(cachePathsState); + if ( + !Array.isArray(cachePaths) || + !cachePaths.every(cachePath => typeof cachePath === 'string') + ) { + throw new Error('Invalid cache paths retrieved from state.'); + } + return cachePaths; +} + /** * State keys used to carry an additional cache's restore-time information over * to the post (save) action, scoped by the additional cache name. @@ -189,11 +213,17 @@ async function computeAdditionalCacheKey( /** * Restore the dependency cache - * @param id ID of the package manager, should be "maven" or "gradle" + * @param id ID of the package manager, should be "maven", "gradle", or "sbt" * @param cacheDependencyPath The path to a dependency file + * @param cachePaths Paths to cache instead of the package manager defaults */ -export async function restore(id: string, cacheDependencyPath: string) { +export async function restore( + id: string, + cacheDependencyPath: string, + cachePaths: string[] = [] +) { const packageManager = findPackageManager(id); + const resolvedCachePaths = resolveCachePaths(packageManager, cachePaths); const [primaryKey, preparedAdditionalCaches] = await Promise.all([ computeCacheKey(packageManager, cacheDependencyPath), prepareAdditionalCaches(packageManager.additionalCaches ?? []) @@ -201,6 +231,7 @@ export async function restore(id: string, cacheDependencyPath: string) { core.debug(`primary key is ${primaryKey}`); core.saveState(STATE_CACHE_PRIMARY_KEY, primaryKey); + core.saveState(STATE_CACHE_PATHS, JSON.stringify(resolvedCachePaths)); core.setOutput(STATE_CACHE_PRIMARY_KEY, primaryKey); for (const preparedCache of preparedAdditionalCaches) { @@ -214,7 +245,7 @@ export async function restore(id: string, cacheDependencyPath: string) { } await Promise.all([ - restorePrimaryCache(packageManager, primaryKey), + restorePrimaryCache(packageManager, resolvedCachePaths, primaryKey), ...preparedAdditionalCaches.map(preparedCache => restoreAdditionalCache(preparedCache) ) @@ -223,10 +254,11 @@ export async function restore(id: string, cacheDependencyPath: string) { async function restorePrimaryCache( packageManager: PackageManager, + cachePaths: string[], primaryKey: string ) { // No "restoreKeys" is set, to start with a clear cache after dependency update (see https://github.com/actions/setup-java/issues/269) - const matchedKey = await cache.restoreCache(packageManager.path, primaryKey); + const matchedKey = await cache.restoreCache(cachePaths, primaryKey); if (matchedKey) { core.saveState(CACHE_MATCHED_KEY, matchedKey); core.setOutput('cache-hit', matchedKey === primaryKey); @@ -286,6 +318,7 @@ async function restoreAdditionalCache(preparedCache: PreparedAdditionalCache) { */ export async function save(id: string) { const packageManager = findPackageManager(id); + const cachePaths = getCachePathsFromState(packageManager); const matchedKey = core.getState(CACHE_MATCHED_KEY); // Inputs are re-evaluated before the post action, so we want the original key used for restore @@ -313,7 +346,7 @@ export async function save(id: string) { return; } try { - const cacheId = await cache.saveCache(packageManager.path, primaryKey); + const cacheId = await cache.saveCache(cachePaths, primaryKey); if (cacheId === -1) { // saveCache returns -1 without throwing when the cache was not saved, // e.g. a reserve collision or a read-only token (fork PR). @actions/cache diff --git a/src/constants.ts b/src/constants.ts index 81b29861..e95bae18 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -38,6 +38,7 @@ export const GPG_PASSPHRASE_PROFILE_ID = 'setup-java-gpg'; export const INPUT_CACHE = 'cache'; export const INPUT_CACHE_DEPENDENCY_PATH = 'cache-dependency-path'; +export const INPUT_CACHE_PATH = 'cache-path'; export const INPUT_CACHE_READ_ONLY = 'cache-read-only'; export const INPUT_JOB_STATUS = 'job-status'; diff --git a/src/setup-java.ts b/src/setup-java.ts index 8945ede3..c3f54f9b 100644 --- a/src/setup-java.ts +++ b/src/setup-java.ts @@ -28,6 +28,7 @@ export async function run() { const cacheDependencyPath = core.getInput( constants.INPUT_CACHE_DEPENDENCY_PATH ); + const cachePath = core.getMultilineInput(constants.INPUT_CACHE_PATH); const checkLatest = getBooleanInput(constants.INPUT_CHECK_LATEST, false); const forceDownload = getBooleanInput( constants.INPUT_FORCE_DOWNLOAD, @@ -132,7 +133,7 @@ export async function run() { await auth.configureAuthentication(); configureMavenArgs(); if (cache && isCacheFeatureAvailable()) { - await restore(cache, cacheDependencyPath); + await restore(cache, cacheDependencyPath, cachePath); } } catch (error) { core.setFailed((error as Error).message);