From 9eda6b51cc4f6ee99be3dd5537b85e389e47bda9 Mon Sep 17 00:00:00 2001 From: itchyny Date: Thu, 23 Nov 2023 00:43:14 +0900 Subject: [PATCH] feat: implement cache-dependency-path option to control caching dependency (#499) --- .../workflows/e2e-cache-dependency-path.yml | 93 +++++++++++++++ .github/workflows/e2e-cache.yml | 2 +- README.md | 12 ++ __tests__/cache.test.ts | 108 ++++++++++++++---- .../cache/{gradle => gradle1}/.gitignore | 0 .../cache/{gradle => gradle1}/build.gradle | 0 __tests__/cache/gradle2/.gitignore | 12 ++ __tests__/cache/gradle2/build.gradle | 17 +++ action.yml | 3 + dist/cleanup/index.js | 24 ++-- dist/setup/index.js | 27 +++-- src/cache.ts | 31 ++--- src/constants.ts | 1 + src/setup-java.ts | 5 +- 14 files changed, 277 insertions(+), 58 deletions(-) create mode 100644 .github/workflows/e2e-cache-dependency-path.yml rename __tests__/cache/{gradle => gradle1}/.gitignore (100%) rename __tests__/cache/{gradle => gradle1}/build.gradle (100%) create mode 100644 __tests__/cache/gradle2/.gitignore create mode 100644 __tests__/cache/gradle2/build.gradle diff --git a/.github/workflows/e2e-cache-dependency-path.yml b/.github/workflows/e2e-cache-dependency-path.yml new file mode 100644 index 000000000..23246a202 --- /dev/null +++ b/.github/workflows/e2e-cache-dependency-path.yml @@ -0,0 +1,93 @@ +name: Validate cache with cache-dependency-path option + +on: + push: + branches: + - main + - releases/* + paths-ignore: + - '**.md' + pull_request: + paths-ignore: + - '**.md' + +defaults: + run: + shell: bash + +jobs: + gradle1-save: + runs-on: ${{ matrix.os }} + 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 gradle + uses: ./ + id: setup-java + with: + distribution: 'adopt' + java-version: '11' + cache: gradle + cache-dependency-path: __tests__/cache/gradle1/*.gradle* + - name: Create files to cache + # Need to avoid using Gradle daemon to stabilize the save process on Windows + # https://github.com/actions/cache/issues/454#issuecomment-840493935 + run: | + gradle downloadDependencies --no-daemon -p __tests__/cache/gradle1 + if [ ! -d ~/.gradle/caches ]; then + echo "::error::The ~/.gradle/caches directory does not exist unexpectedly" + exit 1 + fi + gradle1-restore: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [macos-latest, windows-latest, ubuntu-latest] + needs: gradle1-save + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Run setup-java with the cache for gradle + uses: ./ + id: setup-java + with: + distribution: 'adopt' + java-version: '11' + cache: gradle + cache-dependency-path: __tests__/cache/gradle1/*.gradle* + - name: Confirm that ~/.gradle/caches directory has been made + run: | + if [ ! -d ~/.gradle/caches ]; then + echo "::error::The ~/.gradle/caches directory does not exist unexpectedly" + exit 1 + fi + ls ~/.gradle/caches/ + gradle2-restore: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [macos-latest, windows-latest, ubuntu-latest] + needs: gradle1-save + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Run setup-java with the cache for gradle + uses: ./ + id: setup-java + with: + distribution: 'adopt' + java-version: '11' + cache: gradle + cache-dependency-path: __tests__/cache/gradle2/*.gradle* + - name: Confirm that ~/.gradle/caches directory has not been made + run: | + if [ -d ~/.gradle/caches ]; then + echo "::error::The ~/.gradle/caches directory exists unexpectedly" + exit 1 + fi diff --git a/.github/workflows/e2e-cache.yml b/.github/workflows/e2e-cache.yml index e66e80405..34a07a5fe 100644 --- a/.github/workflows/e2e-cache.yml +++ b/.github/workflows/e2e-cache.yml @@ -36,7 +36,7 @@ jobs: # Need to avoid using Gradle daemon to stabilize the save process on Windows # https://github.com/actions/cache/issues/454#issuecomment-840493935 run: | - gradle downloadDependencies --no-daemon -p __tests__/cache/gradle + gradle downloadDependencies --no-daemon -p __tests__/cache/gradle1 if [ ! -d ~/.gradle/caches ]; then echo "::error::The ~/.gradle/caches directory does not exist unexpectedly" exit 1 diff --git a/README.md b/README.md index 2a333089d..beecd71d4 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,8 @@ This action allows you to work with Java and Scala projects. - `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". + - `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. + #### Maven options 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. @@ -115,10 +117,13 @@ Currently, the following distributions are supported: ### Caching packages dependencies 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: + - gradle: `**/*.gradle*`, `**/gradle-wrapper.properties`, `buildSrc/**/Versions.kt`, `buildSrc/**/Dependencies.kt`, `gradle/*.versions.toml`, and `**/versions.properties` - maven: `**/pom.xml` - sbt: all sbt build definition files `**/*.sbt`, `**/project/build.properties`, `**/project/**.scala`, `**/project/**.sbt` +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. + 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 cache input is optional, and caching is turned off by default. @@ -132,6 +137,9 @@ steps: distribution: 'temurin' java-version: '17' cache: 'gradle' + cache-dependency-path: | # optional + sub-project/*.gradle* + sub-project/**/gradle-wrapper.properties - run: ./gradlew build --no-daemon ``` @@ -144,6 +152,7 @@ steps: distribution: 'temurin' java-version: '17' cache: 'maven' + cache-dependency-path: 'sub-project/pom.xml' # optional - name: Build with Maven run: mvn -B package --file pom.xml ``` @@ -157,6 +166,9 @@ steps: distribution: 'temurin' java-version: '17' cache: 'sbt' + cache-dependency-path: | # optional + sub-project/build.sbt + sub-project/project/build.properties - name: Build with SBT run: sbt package ``` diff --git a/__tests__/cache.test.ts b/__tests__/cache.test.ts index f6932a7cc..9762fb983 100644 --- a/__tests__/cache.test.ts +++ b/__tests__/cache.test.ts @@ -6,6 +6,7 @@ import * as fs from 'fs'; import * as os from 'os'; import * as core from '@actions/core'; import * as cache from '@actions/cache'; +import * as glob from '@actions/glob'; describe('dependency cache', () => { const ORIGINAL_RUNNER_OS = process.env['RUNNER_OS']; @@ -64,6 +65,10 @@ describe('dependency cache', () => { ReturnType, Parameters >; + let spyGlobHashFiles: jest.SpyInstance< + ReturnType, + Parameters + >; beforeEach(() => { spyCacheRestore = jest @@ -71,18 +76,19 @@ describe('dependency cache', () => { .mockImplementation((paths: string[], primaryKey: string) => Promise.resolve(undefined) ); + spyGlobHashFiles = jest.spyOn(glob, 'hashFiles'); spyWarning.mockImplementation(() => null); }); it('throws error if unsupported package manager specified', () => { - return expect(restore('ant')).rejects.toThrow( + return expect(restore('ant', '')).rejects.toThrow( 'unknown package manager specified: ant' ); }); describe('for maven', () => { it('throws error if no pom.xml found', async () => { - await expect(restore('maven')).rejects.toThrow( + await expect(restore('maven', '')).rejects.toThrow( `No file in ${projectRoot( workspace )} matched to [**/pom.xml], make sure you have checked out the target repository` @@ -91,15 +97,16 @@ describe('dependency cache', () => { it('downloads cache', async () => { createFile(join(workspace, 'pom.xml')); - await restore('maven'); + await restore('maven', ''); expect(spyCacheRestore).toHaveBeenCalled(); + expect(spyGlobHashFiles).toHaveBeenCalledWith('**/pom.xml'); expect(spyWarning).not.toHaveBeenCalled(); expect(spyInfo).toHaveBeenCalledWith('maven cache is not found'); }); }); describe('for gradle', () => { it('throws error if no build.gradle found', async () => { - await expect(restore('gradle')).rejects.toThrow( + await expect(restore('gradle', '')).rejects.toThrow( `No file in ${projectRoot( workspace )} 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,16 +115,22 @@ describe('dependency cache', () => { it('downloads cache based on build.gradle', async () => { createFile(join(workspace, 'build.gradle')); - await restore('gradle'); + await restore('gradle', ''); expect(spyCacheRestore).toHaveBeenCalled(); + expect(spyGlobHashFiles).toHaveBeenCalledWith( + '**/*.gradle*\n**/gradle-wrapper.properties\nbuildSrc/**/Versions.kt\nbuildSrc/**/Dependencies.kt\ngradle/*.versions.toml\n**/versions.properties' + ); expect(spyWarning).not.toHaveBeenCalled(); expect(spyInfo).toHaveBeenCalledWith('gradle cache is not found'); }); it('downloads cache based on build.gradle.kts', async () => { createFile(join(workspace, 'build.gradle.kts')); - await restore('gradle'); + await restore('gradle', ''); expect(spyCacheRestore).toHaveBeenCalled(); + expect(spyGlobHashFiles).toHaveBeenCalledWith( + '**/*.gradle*\n**/gradle-wrapper.properties\nbuildSrc/**/Versions.kt\nbuildSrc/**/Dependencies.kt\ngradle/*.versions.toml\n**/versions.properties' + ); expect(spyWarning).not.toHaveBeenCalled(); expect(spyInfo).toHaveBeenCalledWith('gradle cache is not found'); }); @@ -125,24 +138,30 @@ describe('dependency cache', () => { createDirectory(join(workspace, 'gradle')); createFile(join(workspace, 'gradle', 'libs.versions.toml')); - await restore('gradle'); + await restore('gradle', ''); expect(spyCacheRestore).toHaveBeenCalled(); + expect(spyGlobHashFiles).toHaveBeenCalledWith( + '**/*.gradle*\n**/gradle-wrapper.properties\nbuildSrc/**/Versions.kt\nbuildSrc/**/Dependencies.kt\ngradle/*.versions.toml\n**/versions.properties' + ); expect(spyWarning).not.toHaveBeenCalled(); expect(spyInfo).toHaveBeenCalledWith('gradle cache is not found'); }); - }); - it('downloads cache based on buildSrc/Versions.kt', async () => { - createDirectory(join(workspace, 'buildSrc')); - createFile(join(workspace, 'buildSrc', 'Versions.kt')); + it('downloads cache based on buildSrc/Versions.kt', async () => { + createDirectory(join(workspace, 'buildSrc')); + createFile(join(workspace, 'buildSrc', 'Versions.kt')); - await restore('gradle'); - expect(spyCacheRestore).toHaveBeenCalled(); - expect(spyWarning).not.toHaveBeenCalled(); - expect(spyInfo).toHaveBeenCalledWith('gradle cache is not found'); + await restore('gradle', ''); + expect(spyCacheRestore).toHaveBeenCalled(); + expect(spyGlobHashFiles).toHaveBeenCalledWith( + '**/*.gradle*\n**/gradle-wrapper.properties\nbuildSrc/**/Versions.kt\nbuildSrc/**/Dependencies.kt\ngradle/*.versions.toml\n**/versions.properties' + ); + expect(spyWarning).not.toHaveBeenCalled(); + expect(spyInfo).toHaveBeenCalledWith('gradle cache is not found'); + }); }); describe('for sbt', () => { it('throws error if no build.sbt found', async () => { - await expect(restore('sbt')).rejects.toThrow( + await expect(restore('sbt', '')).rejects.toThrow( `No file in ${projectRoot( workspace )} 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', () => { it('downloads cache', async () => { createFile(join(workspace, 'build.sbt')); - await restore('sbt'); + await restore('sbt', ''); expect(spyCacheRestore).toHaveBeenCalled(); + expect(spyGlobHashFiles).toHaveBeenCalledWith( + '**/*.sbt\n**/project/build.properties\n**/project/**.scala\n**/project/**.sbt' + ); expect(spyWarning).not.toHaveBeenCalled(); expect(spyInfo).toHaveBeenCalledWith('sbt cache is not found'); }); @@ -161,11 +183,11 @@ describe('dependency cache', () => { createDirectory(join(workspace, 'project')); createFile(join(workspace, 'project/DependenciesV1.scala')); - await restore('sbt'); + await restore('sbt', ''); const firstCall = spySaveState.mock.calls.toString(); spySaveState.mockClear(); - await restore('sbt'); + await restore('sbt', ''); const secondCall = spySaveState.mock.calls.toString(); // Make sure multiple restores produce the same cache @@ -173,7 +195,7 @@ describe('dependency cache', () => { spySaveState.mockClear(); createFile(join(workspace, 'project/DependenciesV2.scala')); - await restore('sbt'); + await restore('sbt', ''); const thirdCall = spySaveState.mock.calls.toString(); expect(firstCall).not.toBe(thirdCall); @@ -182,11 +204,55 @@ describe('dependency cache', () => { it('downloads cache based on versions.properties', async () => { createFile(join(workspace, 'versions.properties')); - await restore('gradle'); + await restore('gradle', ''); expect(spyCacheRestore).toHaveBeenCalled(); + expect(spyGlobHashFiles).toHaveBeenCalledWith( + '**/*.gradle*\n**/gradle-wrapper.properties\nbuildSrc/**/Versions.kt\nbuildSrc/**/Dependencies.kt\ngradle/*.versions.toml\n**/versions.properties' + ); expect(spyWarning).not.toHaveBeenCalled(); expect(spyInfo).toHaveBeenCalledWith('gradle cache is not found'); }); + describe('cache-dependency-path', () => { + it('throws error if no matching dependency file found', async () => { + createFile(join(workspace, 'build.gradle.kts')); + await expect( + restore('gradle', 'sub-project/**/build.gradle.kts') + ).rejects.toThrow( + `No file in ${projectRoot( + workspace + )} matched to [sub-project/**/build.gradle.kts], make sure you have checked out the target repository` + ); + }); + it('downloads cache based on the specified pattern', async () => { + createFile(join(workspace, 'build.gradle.kts')); + createDirectory(join(workspace, 'sub-project1')); + createFile(join(workspace, 'sub-project1', 'build.gradle.kts')); + createDirectory(join(workspace, 'sub-project2')); + createFile(join(workspace, 'sub-project2', 'build.gradle.kts')); + + await restore('gradle', 'build.gradle.kts'); + expect(spyCacheRestore).toHaveBeenCalled(); + expect(spyGlobHashFiles).toHaveBeenCalledWith('build.gradle.kts'); + expect(spyWarning).not.toHaveBeenCalled(); + expect(spyInfo).toHaveBeenCalledWith('gradle cache is not found'); + + await restore('gradle', 'sub-project1/**/*.gradle*\n'); + expect(spyCacheRestore).toHaveBeenCalled(); + expect(spyGlobHashFiles).toHaveBeenCalledWith( + 'sub-project1/**/*.gradle*' + ); + expect(spyWarning).not.toHaveBeenCalled(); + expect(spyInfo).toHaveBeenCalledWith('gradle cache is not found'); + + await restore('gradle', '*.gradle*\nsub-project2/**/*.gradle*\n'); + expect(spyCacheRestore).toHaveBeenCalled(); + expect(spyGlobHashFiles).toHaveBeenCalledWith( + '*.gradle*\nsub-project2/**/*.gradle*' + ); + expect(spyWarning).not.toHaveBeenCalled(); + expect(spyInfo).toHaveBeenCalledWith('gradle cache is not found'); + }); + }); }); describe('save', () => { let spyCacheSave: jest.SpyInstance< diff --git a/__tests__/cache/gradle/.gitignore b/__tests__/cache/gradle1/.gitignore similarity index 100% rename from __tests__/cache/gradle/.gitignore rename to __tests__/cache/gradle1/.gitignore diff --git a/__tests__/cache/gradle/build.gradle b/__tests__/cache/gradle1/build.gradle similarity index 100% rename from __tests__/cache/gradle/build.gradle rename to __tests__/cache/gradle1/build.gradle diff --git a/__tests__/cache/gradle2/.gitignore b/__tests__/cache/gradle2/.gitignore new file mode 100644 index 000000000..85888bb2e --- /dev/null +++ b/__tests__/cache/gradle2/.gitignore @@ -0,0 +1,12 @@ +.gradle +**/build/ +!src/**/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache diff --git a/__tests__/cache/gradle2/build.gradle b/__tests__/cache/gradle2/build.gradle new file mode 100644 index 000000000..a65d41b5f --- /dev/null +++ b/__tests__/cache/gradle2/build.gradle @@ -0,0 +1,17 @@ +plugins { + id 'java' +} +repositories { + mavenCentral() +} +dependencies { + implementation 'org.junit.jupiter:junit-jupiter-api:5.7.2' +} +tasks.register('downloadDependencies') { + doLast { + def total = configurations.compileClasspath.inject (0) { sum, file -> + sum + file.length() + } + println total + } +} diff --git a/action.yml b/action.yml index 2c0f7aba3..afc90a0db 100644 --- a/action.yml +++ b/action.yml @@ -56,6 +56,9 @@ inputs: cache: description: 'Name of the build platform to cache dependencies. It can be "maven", "gradle" or "sbt".' required: false + 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 job-status: description: 'Workaround to pass job status to post job step. This variable is not intended for manual setting' default: ${{ job.status }} diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index f302d0281..a09480805 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -89724,28 +89724,31 @@ function findPackageManager(id) { /** * A function that generates a cache key to use. * Format of the generated key will be "${{ platform }}-${{ id }}-${{ fileHash }}"". - * If there is no file matched to {@link PackageManager.path}, the generated key ends with a dash (-). * @see {@link https://docs.github.com/en/actions/guides/caching-dependencies-to-speed-up-workflows#matching-a-cache-key|spec of cache key} */ -function computeCacheKey(packageManager) { +function computeCacheKey(packageManager, cacheDependencyPath) { return __awaiter(this, void 0, void 0, function* () { - const hash = yield glob.hashFiles(packageManager.pattern.join('\n')); - return `${CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${packageManager.id}-${hash}`; + const pattern = cacheDependencyPath + ? cacheDependencyPath.trim().split('\n') + : packageManager.pattern; + const fileHash = yield glob.hashFiles(pattern.join('\n')); + if (!fileHash) { + throw new Error(`No file in ${process.cwd()} matched to [${pattern}], make sure you have checked out the target repository`); + } + return `${CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${packageManager.id}-${fileHash}`; }); } /** * Restore the dependency cache * @param id ID of the package manager, should be "maven" or "gradle" + * @param cacheDependencyPath The path to a dependency file */ -function restore(id) { +function restore(id, cacheDependencyPath) { return __awaiter(this, void 0, void 0, function* () { const packageManager = findPackageManager(id); - const primaryKey = yield computeCacheKey(packageManager); + const primaryKey = yield computeCacheKey(packageManager, cacheDependencyPath); core.debug(`primary key is ${primaryKey}`); core.saveState(STATE_CACHE_PRIMARY_KEY, primaryKey); - if (primaryKey.endsWith('-')) { - throw new Error(`No file in ${process.cwd()} matched to [${packageManager.pattern}], make sure you have checked out the target repository`); - } // No "restoreKeys" is set, to start with a clear cache after dependency update (see https://github.com/actions/setup-java/issues/269) const matchedKey = yield cache.restoreCache(packageManager.path, primaryKey); if (matchedKey) { @@ -89922,7 +89925,7 @@ else { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; +exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE_DEPENDENCY_PATH = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; exports.MACOS_JAVA_CONTENT_POSTFIX = 'Contents/Home'; exports.INPUT_JAVA_VERSION = 'java-version'; exports.INPUT_JAVA_VERSION_FILE = 'java-version-file'; @@ -89941,6 +89944,7 @@ exports.INPUT_GPG_PASSPHRASE = 'gpg-passphrase'; exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = undefined; exports.INPUT_DEFAULT_GPG_PASSPHRASE = 'GPG_PASSPHRASE'; exports.INPUT_CACHE = 'cache'; +exports.INPUT_CACHE_DEPENDENCY_PATH = 'cache-dependency-path'; exports.INPUT_JOB_STATUS = 'job-status'; exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = 'gpg-private-key-fingerprint'; exports.M2_DIR = '.m2'; diff --git a/dist/setup/index.js b/dist/setup/index.js index a81030cf1..6d46bf3d6 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -124929,28 +124929,31 @@ function findPackageManager(id) { /** * A function that generates a cache key to use. * Format of the generated key will be "${{ platform }}-${{ id }}-${{ fileHash }}"". - * If there is no file matched to {@link PackageManager.path}, the generated key ends with a dash (-). * @see {@link https://docs.github.com/en/actions/guides/caching-dependencies-to-speed-up-workflows#matching-a-cache-key|spec of cache key} */ -function computeCacheKey(packageManager) { +function computeCacheKey(packageManager, cacheDependencyPath) { return __awaiter(this, void 0, void 0, function* () { - const hash = yield glob.hashFiles(packageManager.pattern.join('\n')); - return `${CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${packageManager.id}-${hash}`; + const pattern = cacheDependencyPath + ? cacheDependencyPath.trim().split('\n') + : packageManager.pattern; + const fileHash = yield glob.hashFiles(pattern.join('\n')); + if (!fileHash) { + throw new Error(`No file in ${process.cwd()} matched to [${pattern}], make sure you have checked out the target repository`); + } + return `${CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${packageManager.id}-${fileHash}`; }); } /** * Restore the dependency cache * @param id ID of the package manager, should be "maven" or "gradle" + * @param cacheDependencyPath The path to a dependency file */ -function restore(id) { +function restore(id, cacheDependencyPath) { return __awaiter(this, void 0, void 0, function* () { const packageManager = findPackageManager(id); - const primaryKey = yield computeCacheKey(packageManager); + const primaryKey = yield computeCacheKey(packageManager, cacheDependencyPath); core.debug(`primary key is ${primaryKey}`); core.saveState(STATE_CACHE_PRIMARY_KEY, primaryKey); - if (primaryKey.endsWith('-')) { - throw new Error(`No file in ${process.cwd()} matched to [${packageManager.pattern}], make sure you have checked out the target repository`); - } // No "restoreKeys" is set, to start with a clear cache after dependency update (see https://github.com/actions/setup-java/issues/269) const matchedKey = yield cache.restoreCache(packageManager.path, primaryKey); if (matchedKey) { @@ -125026,7 +125029,7 @@ function isProbablyGradleDaemonProblem(packageManager, error) { "use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; +exports.DISTRIBUTIONS_ONLY_MAJOR_VERSION = exports.INPUT_MVN_TOOLCHAIN_VENDOR = exports.INPUT_MVN_TOOLCHAIN_ID = exports.MVN_TOOLCHAINS_FILE = exports.MVN_SETTINGS_FILE = exports.M2_DIR = exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = exports.INPUT_JOB_STATUS = exports.INPUT_CACHE_DEPENDENCY_PATH = exports.INPUT_CACHE = exports.INPUT_DEFAULT_GPG_PASSPHRASE = exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = exports.INPUT_GPG_PASSPHRASE = exports.INPUT_GPG_PRIVATE_KEY = exports.INPUT_OVERWRITE_SETTINGS = exports.INPUT_SETTINGS_PATH = exports.INPUT_SERVER_PASSWORD = exports.INPUT_SERVER_USERNAME = exports.INPUT_SERVER_ID = exports.INPUT_CHECK_LATEST = exports.INPUT_JDK_FILE = exports.INPUT_DISTRIBUTION = exports.INPUT_JAVA_PACKAGE = exports.INPUT_ARCHITECTURE = exports.INPUT_JAVA_VERSION_FILE = exports.INPUT_JAVA_VERSION = exports.MACOS_JAVA_CONTENT_POSTFIX = void 0; exports.MACOS_JAVA_CONTENT_POSTFIX = 'Contents/Home'; exports.INPUT_JAVA_VERSION = 'java-version'; exports.INPUT_JAVA_VERSION_FILE = 'java-version-file'; @@ -125045,6 +125048,7 @@ exports.INPUT_GPG_PASSPHRASE = 'gpg-passphrase'; exports.INPUT_DEFAULT_GPG_PRIVATE_KEY = undefined; exports.INPUT_DEFAULT_GPG_PASSPHRASE = 'GPG_PASSPHRASE'; exports.INPUT_CACHE = 'cache'; +exports.INPUT_CACHE_DEPENDENCY_PATH = 'cache-dependency-path'; exports.INPUT_JOB_STATUS = 'job-status'; exports.STATE_GPG_PRIVATE_KEY_FINGERPRINT = 'gpg-private-key-fingerprint'; exports.M2_DIR = '.m2'; @@ -127061,6 +127065,7 @@ function run() { const packageType = core.getInput(constants.INPUT_JAVA_PACKAGE); const jdkFile = core.getInput(constants.INPUT_JDK_FILE); const cache = core.getInput(constants.INPUT_CACHE); + const cacheDependencyPath = core.getInput(constants.INPUT_CACHE_DEPENDENCY_PATH); const checkLatest = util_1.getBooleanInput(constants.INPUT_CHECK_LATEST, false); let toolchainIds = core.getMultilineInput(constants.INPUT_MVN_TOOLCHAIN_ID); core.startGroup('Installed distributions'); @@ -127096,7 +127101,7 @@ function run() { core.info(`##[add-matcher]${path.join(matchersPath, 'java.json')}`); yield auth.configureAuthentication(); if (cache && util_1.isCacheFeatureAvailable()) { - yield cache_1.restore(cache); + yield cache_1.restore(cache, cacheDependencyPath); } } catch (error) { diff --git a/src/cache.ts b/src/cache.ts index 7dd8da9a9..a4b5a4cad 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -83,31 +83,34 @@ function findPackageManager(id: string): PackageManager { /** * A function that generates a cache key to use. * Format of the generated key will be "${{ platform }}-${{ id }}-${{ fileHash }}"". - * If there is no file matched to {@link PackageManager.path}, the generated key ends with a dash (-). * @see {@link https://docs.github.com/en/actions/guides/caching-dependencies-to-speed-up-workflows#matching-a-cache-key|spec of cache key} */ -async function computeCacheKey(packageManager: PackageManager) { - const hash = await glob.hashFiles(packageManager.pattern.join('\n')); - return `${CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${packageManager.id}-${hash}`; +async function computeCacheKey( + packageManager: PackageManager, + cacheDependencyPath: string +) { + const pattern = cacheDependencyPath + ? cacheDependencyPath.trim().split('\n') + : packageManager.pattern; + const fileHash = await glob.hashFiles(pattern.join('\n')); + if (!fileHash) { + throw new Error( + `No file in ${process.cwd()} matched to [${pattern}], make sure you have checked out the target repository` + ); + } + return `${CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${packageManager.id}-${fileHash}`; } /** * Restore the dependency cache * @param id ID of the package manager, should be "maven" or "gradle" + * @param cacheDependencyPath The path to a dependency file */ -export async function restore(id: string) { +export async function restore(id: string, cacheDependencyPath: string) { const packageManager = findPackageManager(id); - const primaryKey = await computeCacheKey(packageManager); - + const primaryKey = await computeCacheKey(packageManager, cacheDependencyPath); core.debug(`primary key is ${primaryKey}`); core.saveState(STATE_CACHE_PRIMARY_KEY, primaryKey); - if (primaryKey.endsWith('-')) { - throw new Error( - `No file in ${process.cwd()} matched to [${ - packageManager.pattern - }], make sure you have checked out the target repository` - ); - } // 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); diff --git a/src/constants.ts b/src/constants.ts index 9aa213fa9..93af286f8 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -18,6 +18,7 @@ export const INPUT_DEFAULT_GPG_PRIVATE_KEY = undefined; export const INPUT_DEFAULT_GPG_PASSPHRASE = 'GPG_PASSPHRASE'; export const INPUT_CACHE = 'cache'; +export const INPUT_CACHE_DEPENDENCY_PATH = 'cache-dependency-path'; export const INPUT_JOB_STATUS = 'job-status'; export const STATE_GPG_PRIVATE_KEY_FINGERPRINT = 'gpg-private-key-fingerprint'; diff --git a/src/setup-java.ts b/src/setup-java.ts index 4168d53d1..68d234f38 100644 --- a/src/setup-java.ts +++ b/src/setup-java.ts @@ -24,6 +24,9 @@ async function run() { const packageType = core.getInput(constants.INPUT_JAVA_PACKAGE); const jdkFile = core.getInput(constants.INPUT_JDK_FILE); const cache = core.getInput(constants.INPUT_CACHE); + const cacheDependencyPath = core.getInput( + constants.INPUT_CACHE_DEPENDENCY_PATH + ); const checkLatest = getBooleanInput(constants.INPUT_CHECK_LATEST, false); let toolchainIds = core.getMultilineInput(constants.INPUT_MVN_TOOLCHAIN_ID); @@ -73,7 +76,7 @@ async function run() { await auth.configureAuthentication(); if (cache && isCacheFeatureAvailable()) { - await restore(cache); + await restore(cache, cacheDependencyPath); } } catch (error) { core.setFailed(error.message);