diff --git a/README.md b/README.md index 9ec3545e..eb80558f 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ steps: - [Inputs](#inputs) - [Supported distributions](#supported-distributions) - [Supported version syntax](#supported-version-syntax) -- [Caching dependencies](#caching-dependencies) +- [Caching](#caching) - [Multiple JDKs and Maven toolchains](#multiple-jdks-and-maven-toolchains) - [Publishing packages](#publishing-packages) - [Advanced usage](#advanced-usage) @@ -61,6 +61,7 @@ steps: - JDK downloads now automatically verify authoritative checksums for [supported distributions](#download-integrity-and-signatures). - Added `force-download: true` to bypass the tool cache and perform a reproducible fresh install. - Dependency caching now supports custom paths with `cache-path` and restore-only operation with `cache-read-only: true`. +- Downloaded JDKs are now [cached](#caching-jdk-installations) automatically when `cache` is set; use `cache-jdk` to enable or disable it independently. - Set `problem-matcher: false` to disable Java compiler and uncaught-exception annotations. - GraalVM distributions now set `GRAALVM_HOME` in addition to `JAVA_HOME`. - Invalid boolean values, unsupported distribution/package/platform combinations, and mismatched Maven toolchain ID counts now fail with targeted errors. @@ -161,9 +162,10 @@ steps: | `verify-signature-public-key` | ASCII-armored GPG public key to use for signature verification. Overrides the bundled key. | | | `token` | Token for fetching GitHub.com-hosted version manifests, useful on GitHub Enterprise Server when unauthenticated requests are rate-limited. | `${{ github.token }}` on GitHub.com; empty string on GHES | | `cache` | Enable dependency caching for `maven`, `gradle`, or `sbt`. | | +| `cache-jdk` | Cache downloaded JDK installations between jobs. When omitted, JDK caching is enabled only if `cache` is set. Set explicitly to `true` or `false` to override. | Enabled when `cache` is set | | `cache-dependency-path` | Dependency file paths used for cache key hashing. Supports globs and multiline values. | Auto-detected by package manager | | `cache-path` | Cache paths to use instead of the package manager's default dependency cache path. Supports multiline values and exclusions. | | -| `cache-read-only` | Restore caches without saving changes in the post step. | `false` | +| `cache-read-only` | Restore dependency, wrapper, and JDK caches without saving changes in the post step. | `false` | | `server-id` | Maven repository ID used in generated `settings.xml`. | `github` | | `server-username-env-var` | Environment variable name for Maven repository username. | `GITHUB_ACTOR` | | `server-password-env-var` | Environment variable name for Maven repository password or token. | `GITHUB_TOKEN` | @@ -175,6 +177,8 @@ steps: | `mvn-toolchain-vendor` | Maven Toolchain vendor value. | `${distribution}` | | `show-download-progress` | Keep Maven artifact download and transfer progress in logs. When `false`, the action adds `-ntp` to `MAVEN_ARGS`. | `false` | +- `java-package`: Supported package types are `jdk`, `jre`, `jdk+fx`, `jre+fx`, `jdk+crac`, `jre+crac`, `jdk+jmods`, `jdk+jcef`, `jre+jcef`, `jdk+ft`, and `jre+ft`. Availability varies by distribution. + Deprecated aliases `jdkFile`, `server-username`, `server-password`, and `gpg-passphrase` remain accepted for compatibility, but should be replaced with the current input names. ## Outputs @@ -238,11 +242,19 @@ GitHub-hosted runners primarily pre-cache Eclipse Temurin JDKs. See the installe `setup-java` automatically verifies downloaded archive checksums when a selected distribution publishes an authoritative checksum. Automatic checksum verification currently applies to `temurin`, `semeru`, `corretto`, `dragonwell`, `kona`, `sapmachine`, `graalvm`, `graalvm-community`, `zulu`, `oracle`, `oracle-openjdk`, `microsoft`, and `jetbrains`. -Distributions or individual releases without an authoritative checksum continue to install normally, with the omission reported in debug logs. Archives resolved directly from the runner tool cache are not downloaded again and are not reverified. +Distributions or individual releases without an authoritative checksum continue to install normally, with the omission reported in debug logs. Installations resolved directly from the runner tool cache — including JDKs preinstalled on the runner image and JDKs installed by an earlier step of the same job — are not downloaded again and are not reverified, even when `verify-signature: true` is set. Use `force-download: true` to always download and verify the archive. Use `verify-signature: true` to verify package signatures for distributions that support it. Currently supported distributions are `temurin` and `microsoft`; setting it for an unsupported distribution fails the workflow. -## Caching dependencies +## Caching + +`setup-java` manages three kinds of caches. Each one is restored and saved as a separate cache entry. + +| Cache | What it stores | Key based on | How it is enabled | +| --- | --- | --- | --- | +| Dependency cache | Downloaded dependencies, such as `~/.m2/repository`, `~/.gradle/caches`, or the sbt cache paths | Runner OS, architecture, package manager, and a hash of the dependency files | Set `cache` to `maven`, `gradle`, or `sbt` | +| Wrapper caches | Maven and Gradle wrapper distributions (`~/.m2/wrapper/dists`, `~/.gradle/wrapper`) | Runner OS, architecture, wrapper cache name, and a hash of the wrapper properties | Set `cache` to `maven` or `gradle` | +| JDK cache | The downloaded JDK installation | Runner OS, architecture, distribution, package type, resolved version, release identity, and signature-verification identity | Enabled implicitly whenever `cache` is set, or explicitly with `cache-jdk: true`. Opt out with `cache-jdk: false` | Set `cache` to `maven`, `gradle`, or `sbt` to cache dependencies with minimal configuration. @@ -294,9 +306,30 @@ Use `cache-path` when the build tool stores dependencies outside the default loc `cache-path` changes what is restored and saved, but not the cache key. Jobs that should share a cache key must use the same OS, architecture, package manager, dependency files, and cache paths. +### Wrapper caches + +Maven and Gradle wrapper distributions are restored and saved as additional cache entries, separate from the primary dependency cache. These entries have their own keys in the form `setup-java----`. + +| Package manager | Wrapper cache name | Cached path | Files used for wrapper-cache key | +| --- | --- | --- | --- | +| Maven | `maven-wrapper` | `~/.m2/wrapper/dists` | `**/.mvn/wrapper/maven-wrapper.properties` | +| Gradle | `gradle-wrapper` | `~/.gradle/wrapper` | `**/gradle-wrapper.properties` | + +These wrapper caches are independent from dependency caches, so they remain useful even when dependency files change frequently. The wrapper properties are also part of the Maven and Gradle primary dependency-cache key because wrapper changes can affect how dependencies are resolved, but the wrapper distribution files themselves are stored in the separate wrapper cache entries above. + +For advanced Gradle caching features such as build output caching, configuration cache support, encrypted cache storage, cleanup, and fine-grained cache control, consider [`gradle/actions/setup-gradle`](https://github.com/gradle/actions/tree/main/setup-gradle). + +### Caching JDK installations + +The JDK cache stores the downloaded JDK installation so later runs skip the download. It is enabled implicitly whenever dependency `cache` is set, so most workflows that cache dependencies are already caching the JDK. Set `cache-jdk: true` to enable it without dependency caching, or `cache-jdk: false` to opt out while keeping dependency caching. With neither `cache` nor `cache-jdk` set, nothing is cached. + +> [!IMPORTANT] +> Because JDK caching is on by default whenever `cache` is set, review [Caching JDK installations](docs/advanced-usage.md#caching-jdk-installations) +> for the full `cache`/`cache-jdk` matrix, cache identity and storage impact. + ### Read-only caches -Set `cache-read-only: true` to restore dependency caches without saving changes in the post action. This is useful for pull requests, merge queues, short-lived branches, and matrix fan-out jobs that should only consume caches produced elsewhere. +Set `cache-read-only: true` to restore dependency, wrapper, and JDK caches without saving changes in the post action. This is useful for pull requests, merge queues, short-lived branches, and matrix fan-out jobs that should only consume caches produced elsewhere. ```yaml - uses: actions/setup-java@v5 @@ -339,19 +372,6 @@ jobs: - run: mvn ${{ matrix.goal }} ``` -### Wrapper caches - -Maven and Gradle wrapper distributions are restored and saved as additional cache entries, separate from the primary dependency cache. These entries have their own keys in the form `setup-java----`. - -| Package manager | Wrapper cache name | Cached path | Files used for wrapper-cache key | -| --- | --- | --- | --- | -| Maven | `maven-wrapper` | `~/.m2/wrapper/dists` | `**/.mvn/wrapper/maven-wrapper.properties` | -| Gradle | `gradle-wrapper` | `~/.gradle/wrapper` | `**/gradle-wrapper.properties` | - -These wrapper caches are independent from dependency caches, so they remain useful even when dependency files change frequently. The wrapper properties are also part of the Maven and Gradle primary dependency-cache key because wrapper changes can affect how dependencies are resolved, but the wrapper distribution files themselves are stored in the separate wrapper cache entries above. - -For advanced Gradle caching features such as build output caching, configuration cache support, encrypted cache storage, cleanup, and fine-grained cache control, consider [`gradle/actions/setup-gradle`](https://github.com/gradle/actions/tree/main/setup-gradle). - ### Cache segment restore timeout Cache downloads are split into segments. To reduce the chance of a stuck segment blocking a workflow, set `SEGMENT_DOWNLOAD_TIMEOUT_MINS`: diff --git a/__tests__/cleanup-java.test.ts b/__tests__/cleanup-java.test.ts index 8a4c0458..82f7b367 100644 --- a/__tests__/cleanup-java.test.ts +++ b/__tests__/cleanup-java.test.ts @@ -8,6 +8,9 @@ import { beforeAll, afterAll } from '@jest/globals'; +import fs from 'fs'; +import os from 'os'; +import path from 'path'; // Mock @actions/cache before importing source modules const real_cache_module = await import('@actions/cache'); @@ -60,6 +63,9 @@ const core = await import('@actions/core'); const cache = await import('@actions/cache'); const {run: cleanup} = await import('../src/cleanup-java.js'); const util = await import('../src/util.js'); +const {registerJdk, buildJdkCacheKey} = await import('../src/jdk-cache.js'); + +const jdkTempRoots: string[] = []; describe('cleanup', () => { let spyWarning: any; @@ -88,6 +94,9 @@ describe('cleanup', () => { }); afterEach(() => { + while (jdkTempRoots.length) { + fs.rmSync(jdkTempRoots.pop()!, {recursive: true, force: true}); + } resetState(); jest.resetAllMocks(); jest.clearAllMocks(); @@ -163,6 +172,103 @@ describe('cleanup', () => { expect(spyCacheSave).toHaveBeenCalled(); }); + + it('saves the JDK cache without dependency caching', async () => { + const {key, path: jdkPath, state} = createRegisteredJdk(); + (core.getInput as jest.Mock).mockImplementation((name: string) => + name === 'cache-jdk' ? 'true' : '' + ); + (core.getState as jest.Mock).mockImplementation((name: string) => + name === 'jdk-caches' ? state : '' + ); + spyCacheSave.mockResolvedValue(1); + + await cleanup(); + + expect(spyCacheSave).toHaveBeenCalledWith([jdkPath], key); + }); + + it('does not save a JDK cache when cache-jdk is disabled', async () => { + (core.getInput as jest.Mock).mockImplementation((name: string) => + name === 'cache-jdk' ? 'false' : '' + ); + + await cleanup(); + + expect(spyCacheSave).not.toHaveBeenCalled(); + }); + + it.each([ + ['', '', false], + ['', 'true', true], + ['', 'false', false], + ['maven', '', true], + ['maven', 'true', true], + ['maven', 'false', false] + ])( + 'uses effective JDK caching for cache=%j and cache-jdk=%j', + async (cacheInput, cacheJdkInput, expectedJdkSave) => { + const {key: jdkKey, path: jdkPath, state} = createRegisteredJdk(); + (core.getInput as jest.Mock).mockImplementation((name: string) => { + if (name === 'cache') return cacheInput; + if (name === 'cache-jdk') return cacheJdkInput; + return ''; + }); + (core.getState as jest.Mock).mockImplementation((name: string) => + name === 'jdk-caches' ? state : '' + ); + spyCacheSave.mockResolvedValue(1); + + await cleanup(); + + const jdkSaveCalls = spyCacheSave.mock.calls.filter( + ([, key]) => key === jdkKey + ); + expect(jdkSaveCalls).toHaveLength(expectedJdkSave ? 1 : 0); + if (expectedJdkSave) { + expect(spyCacheSave).toHaveBeenCalledWith([jdkPath], jdkKey); + } + } + ); + + it('keeps saving the remaining JDK caches when one save fails', async () => { + const first = createRegisteredJdk(); + const second = createRegisteredJdk('17.0.19+9'); + (core.getInput as jest.Mock).mockImplementation((name: string) => + name === 'cache-jdk' ? 'true' : '' + ); + (core.getState as jest.Mock).mockImplementation((name: string) => + name === 'jdk-caches' ? second.state : '' + ); + spyCacheSave.mockImplementation(async (paths: string[]) => { + if (paths[0] === first.path) { + throw new Error('Unexpected save failure'); + } + return 1; + }); + + await cleanup(); + + expect(spyCacheSave).toHaveBeenCalledWith([first.path], first.key); + expect(spyCacheSave).toHaveBeenCalledWith([second.path], second.key); + expect(spyCoreError).not.toHaveBeenCalled(); + }); + + it('does not save a JDK installation that was replaced after registration', async () => { + const {key, path: jdkPath, state, replace} = createRegisteredJdk(); + (core.getInput as jest.Mock).mockImplementation((name: string) => + name === 'cache-jdk' ? 'true' : '' + ); + (core.getState as jest.Mock).mockImplementation((name: string) => + name === 'jdk-caches' ? state : '' + ); + spyCacheSave.mockResolvedValue(1); + replace(); + + await cleanup(); + + expect(spyCacheSave).not.toHaveBeenCalledWith([jdkPath], key); + }); }); function resetState() { @@ -199,3 +305,49 @@ function createStateForSuccessfulRestoreWithWrapper(packageManager: string) { } }); } + +/** + * Register a real JDK installation in a temporary tool cache so the post-job + * save sees the same installation identity that setup recorded. + */ +function createRegisteredJdk(version = '21.0.8+9') { + const root = fs.mkdtempSync( + path.join(os.tmpdir(), 'setup-java-cleanup-jdk-') + ); + jdkTempRoots.push(root); + const jdkPath = path.join( + root, + 'Java_temurin_jdk', + version.replace('+', '-') + ); + const write = (marker: string) => { + const architecturePath = path.join(jdkPath, 'x64'); + fs.rmSync(architecturePath, {recursive: true, force: true}); + fs.rmSync(`${architecturePath}.complete`, {force: true}); + fs.mkdirSync(architecturePath, {recursive: true}); + fs.writeFileSync(path.join(architecturePath, 'release'), marker); + fs.writeFileSync(`${architecturePath}.complete`, marker); + }; + write('installed'); + + const jdk = { + distribution: 'temurin', + packageType: 'jdk', + architecture: 'x64', + version, + source: `sha256:${path.basename(root)}`, + verification: 'unverified', + path: jdkPath + }; + registerJdk(jdk); + const state = ( + (core.saveState as jest.Mock).mock.calls.at(-1) as string[] + )[1]; + + return { + key: buildJdkCacheKey(jdk), + path: jdkPath, + state, + replace: () => write('replaced-by-a-later-step') + }; +} diff --git a/__tests__/distributors/base-installer.test.ts b/__tests__/distributors/base-installer.test.ts index f8aa0d24..b41ccf4c 100644 --- a/__tests__/distributors/base-installer.test.ts +++ b/__tests__/distributors/base-installer.test.ts @@ -70,6 +70,14 @@ jest.unstable_mockModule('@actions/tool-cache', () => ({ } })); +jest.unstable_mockModule('../../src/jdk-cache.js', () => ({ + getJdkVerificationIdentity: jest.fn((verified: boolean, key?: string) => + verified ? (key ? 'verified:custom' : 'verified:bundled') : 'unverified' + ), + registerJdk: jest.fn(), + restoreJdk: jest.fn() +})); + const real_util_module = await import('../../src/util.js'); jest.unstable_mockModule('../../src/util.js', () => ({ ...real_util_module, @@ -86,6 +94,7 @@ jest.unstable_mockModule('../../src/util.js', () => ({ const core = await import('@actions/core'); const tc = await import('@actions/tool-cache'); const util = await import('../../src/util.js'); +const jdkCache = await import('../../src/jdk-cache.js'); const {JavaBase} = await import('../../src/distributions/base-installer.js'); class EmptyJavaBase extends JavaBase { @@ -336,6 +345,10 @@ describe('setupJava', () => { let spyCoreError: any; beforeEach(() => { + (jdkCache.getJdkVerificationIdentity as jest.Mock).mockImplementation( + (verified: boolean, key?: string) => + verified ? (key ? 'verified:custom' : 'verified:bundled') : 'unverified' + ); spyGetToolcachePath = util.getToolcachePath as jest.Mock; spyGetToolcachePath.mockImplementation( (toolname: string, javaVersion: string, architecture: string) => { @@ -463,8 +476,10 @@ describe('setupJava', () => { architecture: 'x86', packageType: 'jdk', checkLatest: false, - forceDownload: true + forceDownload: true, + cacheJdk: true }); + const findInToolcache = jest.fn(() => ({ version: actualJavaVersion, path: javaPathInstalled @@ -484,6 +499,111 @@ describe('setupJava', () => { expect(spyCoreInfo).not.toHaveBeenCalledWith( `Resolved Java ${actualJavaVersion} from tool-cache` ); + expect(jdkCache.restoreJdk).not.toHaveBeenCalled(); + expect(jdkCache.registerJdk).toHaveBeenCalledWith( + expect.objectContaining({ + version: actualJavaVersion, + verification: 'unverified' + }) + ); + }); + + it.each([ + [false, false, false, false], + [false, true, true, true], + [true, false, false, false], + [true, true, false, true] + ])( + 'handles force-download=%s and cache-jdk=%s', + async (forceDownload, cacheJdkEnabled, restores, registers) => { + mockJavaBase = new EmptyJavaBase({ + version: actualJavaVersion, + architecture: 'x86', + packageType: 'jdk', + checkLatest: true, + forceDownload, + cacheJdk: cacheJdkEnabled + }); + (jdkCache.restoreJdk as jest.Mock).mockResolvedValue(false); + + await mockJavaBase.setupJava(); + + expect(jdkCache.restoreJdk).toHaveBeenCalledTimes(restores ? 1 : 0); + expect(jdkCache.registerJdk).toHaveBeenCalledTimes(registers ? 1 : 0); + } + ); + + it('restores the exact resolved JDK before downloading', async () => { + const toolCachePath = path.join('toolcache'); + jest.replaceProperty(process, 'env', { + ...process.env, + RUNNER_TOOL_CACHE: toolCachePath + }); + mockJavaBase = new EmptyJavaBase({ + version: '11', + architecture: 'x86', + packageType: 'jdk', + checkLatest: true, + cacheJdk: true + }); + const downloadTool = jest.spyOn(mockJavaBase as any, 'downloadTool'); + (jdkCache.restoreJdk as jest.Mock).mockResolvedValue(true); + jest + .spyOn(mockJavaBase as any, 'getRestoredJdkPath') + .mockReturnValue(javaPathInstalled); + + await expect(mockJavaBase.setupJava()).resolves.toEqual({ + version: actualJavaVersion, + path: javaPathInstalled + }); + + expect(jdkCache.restoreJdk).toHaveBeenCalledWith({ + distribution: 'Empty', + packageType: 'jdk', + architecture: 'x86', + version: actualJavaVersion, + source: `some/random_url/java/${actualJavaVersion}`, + verification: 'unverified', + path: path.join(toolCachePath, 'Java_Empty_jdk', actualJavaVersion) + }); + expect(downloadTool).not.toHaveBeenCalled(); + expect(spyCoreInfo).not.toHaveBeenCalledWith('Trying to download...'); + // A restored entry is already stored under its key; it must not be + // re-registered for a post-job save. + expect(jdkCache.registerJdk).not.toHaveBeenCalled(); + }); + + it('registers the downloaded JDK identity after a JDK cache miss', async () => { + const toolCachePath = path.join('toolcache'); + jest.replaceProperty(process, 'env', { + ...process.env, + RUNNER_TOOL_CACHE: toolCachePath + }); + mockJavaBase = new EmptyJavaBase({ + version: '11', + architecture: 'x86', + packageType: 'jdk', + checkLatest: true, + cacheJdk: true + }); + (jdkCache.restoreJdk as jest.Mock).mockResolvedValue(false); + + await mockJavaBase.setupJava(); + + const expectedIdentity = { + distribution: 'Empty', + packageType: 'jdk', + architecture: 'x86', + version: actualJavaVersion, + source: `some/random_url/java/${actualJavaVersion}`, + verification: 'unverified', + path: path.join(toolCachePath, 'Java_Empty_jdk', actualJavaVersion) + }; + expect(jdkCache.restoreJdk).toHaveBeenCalledWith(expectedIdentity); + // Registration happens after the installation exists, so the post-job save + // can detect a later step replacing it. + expect(jdkCache.registerJdk).toHaveBeenCalledWith(expectedIdentity); + expect(spyCoreInfo).toHaveBeenCalledWith('Trying to download...'); }); it.each([ diff --git a/__tests__/distributors/local-installer.test.ts b/__tests__/distributors/local-installer.test.ts index 47997888..f20ff6ad 100644 --- a/__tests__/distributors/local-installer.test.ts +++ b/__tests__/distributors/local-installer.test.ts @@ -12,6 +12,9 @@ import fs from 'fs'; import path from 'path'; import * as semver from 'semver'; +import os from 'os'; + +const realStatSync = fs.statSync; // Mock @actions/core before importing source modules that depend on it jest.unstable_mockModule('@actions/core', () => ({ @@ -54,6 +57,12 @@ jest.unstable_mockModule('@actions/tool-cache', () => ({ evaluateVersions: jest.fn() })); +jest.unstable_mockModule('../../src/jdk-cache.js', () => ({ + getJdkVerificationIdentity: jest.fn(() => 'unverified'), + registerJdk: jest.fn(), + restoreJdk: jest.fn() +})); + const real_util_module = await import('../../src/util.js'); jest.unstable_mockModule('../../src/util.js', () => ({ ...real_util_module, @@ -70,6 +79,7 @@ jest.unstable_mockModule('../../src/util.js', () => ({ const core = await import('@actions/core'); const tc = await import('@actions/tool-cache'); const util = await import('../../src/util.js'); +const jdkCache = await import('../../src/jdk-cache.js'); const {LocalDistribution} = await import('../../src/distributions/local/installer.js'); @@ -95,6 +105,9 @@ describe('setupJava', () => { const expectedJdkFile = 'JavaLocalJdkFile'; beforeEach(() => { + (jdkCache.getJdkVerificationIdentity as jest.Mock).mockReturnValue( + 'unverified' + ); spyGetToolcachePath = util.getToolcachePath as jest.Mock; spyGetToolcachePath.mockImplementation( (toolname: string, javaVersion: string, architecture: string) => { @@ -231,6 +244,72 @@ describe('setupJava', () => { ); }); + it.each([ + [false, true, true], + [true, false, true] + ])( + 'handles jdkfile caching with force-download=%s', + async (forceDownload, restores, registers) => { + const temporaryDirectory = fs.mkdtempSync( + path.join(os.tmpdir(), 'setup-java-local-cache-') + ); + const jdkFile = path.join(temporaryDirectory, 'java.tar.gz'); + fs.writeFileSync(jdkFile, 'jdk archive'); + spyGetToolcachePath.mockReturnValue(''); + spyFsStat.mockImplementation((file: string) => realStatSync(file)); + (jdkCache.restoreJdk as jest.Mock).mockResolvedValue(false); + + try { + mockJavaBase = new LocalDistribution( + { + version: actualJavaVersion, + architecture: 'x86', + packageType: 'jdk', + checkLatest: false, + forceDownload, + cacheJdk: true + }, + jdkFile + ); + + await mockJavaBase.setupJava(); + + expect(jdkCache.restoreJdk).toHaveBeenCalledTimes(restores ? 1 : 0); + expect(jdkCache.registerJdk).toHaveBeenCalledTimes(registers ? 1 : 0); + expect( + (jdkCache.restoreJdk as jest.Mock).mock.calls[0]?.[0] ?? + (jdkCache.registerJdk as jest.Mock).mock.calls[0]?.[0] + ).toEqual( + expect.objectContaining({ + distribution: 'jdkfile', + version: actualJavaVersion, + verification: 'unverified' + }) + ); + } finally { + fs.rmSync(temporaryDirectory, {recursive: true}); + } + } + ); + + it('rejects signature verification for jdkfile archives', async () => { + mockJavaBase = new LocalDistribution( + { + version: actualJavaVersion, + architecture: 'x86', + packageType: 'jdk', + checkLatest: false, + verifySignature: true + }, + expectedJdkFile + ); + + await expect(mockJavaBase.setupJava()).rejects.toThrow( + "Input 'verify-signature' is not supported for distribution 'jdkfile'." + ); + expect(spyGetToolcachePath).not.toHaveBeenCalled(); + }); + it("java is resolved from toolcache, jdkfile doesn't exist", async () => { const inputs = { version: actualJavaVersion, diff --git a/__tests__/jdk-cache.test.ts b/__tests__/jdk-cache.test.ts new file mode 100644 index 00000000..63788a78 --- /dev/null +++ b/__tests__/jdk-cache.test.ts @@ -0,0 +1,325 @@ +import {jest, describe, it, expect, beforeEach, afterEach} from '@jest/globals'; +import fs from 'fs'; +import os from 'os'; +import path from 'path'; + +jest.unstable_mockModule('@actions/cache', () => ({ + restoreCache: jest.fn(), + saveCache: jest.fn(), + ReserveCacheError: class ReserveCacheError extends Error { + constructor(message: string) { + super(message); + this.name = 'ReserveCacheError'; + } + } +})); + +jest.unstable_mockModule('@actions/core', () => ({ + info: jest.fn(), + warning: jest.fn(), + debug: jest.fn(), + saveState: jest.fn(), + getState: jest.fn() +})); + +jest.unstable_mockModule('../src/cache-feature.js', () => ({ + isCacheFeatureAvailable: jest.fn() +})); + +const cache = await import('@actions/cache'); +const core = await import('@actions/core'); +const cacheFeature = await import('../src/cache-feature.js'); +const { + buildJdkCacheKey, + getJdkVerificationIdentity, + registerJdk, + restoreJdk, + saveJdkCaches +} = await import('../src/jdk-cache.js'); + +const jdk = { + distribution: 'temurin', + packageType: 'jdk', + architecture: 'x64', + version: '21.0.8+9', + source: 'sha256:abc123', + verification: 'unverified', + path: '/toolcache/Java_temurin_jdk/21.0.8-9' +}; + +describe('JDK cache', () => { + const tempRoots: string[] = []; + + const createInstallation = (marker = 'a'): string => { + const root = fs.mkdtempSync(path.join(os.tmpdir(), 'setup-java-jdk-')); + tempRoots.push(root); + const jdkPath = path.join(root, 'Java_temurin_jdk', '21.0.8-9'); + writeInstallation(jdkPath, marker); + return jdkPath; + }; + + const writeInstallation = (jdkPath: string, marker: string): void => { + const architecturePath = path.join(jdkPath, 'x64'); + fs.rmSync(architecturePath, {recursive: true, force: true}); + fs.rmSync(`${architecturePath}.complete`, {force: true}); + fs.mkdirSync(path.join(architecturePath, 'bin'), {recursive: true}); + fs.writeFileSync(path.join(architecturePath, 'bin', 'java'), marker); + fs.writeFileSync(`${architecturePath}.complete`, marker); + }; + + const lastState = (): string => + ((core.saveState as jest.Mock).mock.calls.at(-1) as string[])[1]; + + beforeEach(() => { + jest.resetAllMocks(); + (cacheFeature.isCacheFeatureAvailable as jest.Mock).mockReturnValue(true); + process.env['RUNNER_OS'] = 'Linux'; + }); + + afterEach(() => { + jest.restoreAllMocks(); + delete process.env['RUNNER_OS']; + while (tempRoots.length) { + fs.rmSync(tempRoots.pop()!, {recursive: true, force: true}); + } + }); + + it('builds distinct keys for incompatible JDK identities', () => { + const key = buildJdkCacheKey(jdk); + + expect(key).toMatch(/^setup-java-jdk-v1-Linux-x64-[a-f0-9]{64}$/); + expect(buildJdkCacheKey({...jdk, architecture: 'aarch64'})).not.toBe(key); + expect(buildJdkCacheKey({...jdk, distribution: 'zulu'})).not.toBe(key); + expect(buildJdkCacheKey({...jdk, packageType: 'jre'})).not.toBe(key); + expect(buildJdkCacheKey({...jdk, version: '21.0.7+6'})).not.toBe(key); + expect(buildJdkCacheKey({...jdk, source: 'sha256:def456'})).not.toBe(key); + }); + + it('preserves canonical runner OS values and separates operating systems', () => { + process.env['RUNNER_OS'] = 'Linux'; + const linux = buildJdkCacheKey(jdk); + process.env['RUNNER_OS'] = 'Windows'; + const windows = buildJdkCacheKey(jdk); + process.env['RUNNER_OS'] = 'macOS'; + const macos = buildJdkCacheKey(jdk); + + expect(new Set([linux, windows, macos])).toHaveProperty('size', 3); + expect(linux).toMatch(/^setup-java-jdk-v1-Linux-x64-/); + expect(windows).toMatch(/^setup-java-jdk-v1-Windows-x64-/); + expect(macos).toMatch(/^setup-java-jdk-v1-macOS-x64-/); + }); + + it('falls back to process.platform without RUNNER_OS', () => { + delete process.env['RUNNER_OS']; + + expect(buildJdkCacheKey(jdk)).toMatch( + new RegExp(`^setup-java-jdk-v1-${process.platform}-x64-`) + ); + }); + + it('separates unverified, bundled-key, and custom-key caches', () => { + const unverified = getJdkVerificationIdentity(false); + const bundled = getJdkVerificationIdentity(true); + const customA = getJdkVerificationIdentity( + true, + '-----BEGIN PGP PUBLIC KEY BLOCK-----\r\nkey-a\r\n-----END PGP PUBLIC KEY BLOCK-----\r\n' + ); + const customANormalized = getJdkVerificationIdentity( + true, + '-----BEGIN PGP PUBLIC KEY BLOCK-----\nkey-a\n-----END PGP PUBLIC KEY BLOCK-----' + ); + const customB = getJdkVerificationIdentity(true, 'different-key'); + + expect(new Set([unverified, bundled, customA, customB])).toHaveProperty( + 'size', + 4 + ); + expect(customA).toBe(customANormalized); + expect(customA).not.toContain('key-a'); + expect( + new Set( + [unverified, bundled, customA, customB].map(verification => + buildJdkCacheKey({...jdk, verification}) + ) + ) + ).toHaveProperty('size', 4); + }); + + it('restores and records an exact JDK cache hit', async () => { + (cache.restoreCache as jest.Mock).mockResolvedValue(buildJdkCacheKey(jdk)); + jest.spyOn(fs, 'existsSync').mockReturnValue(true); + + await expect(restoreJdk(jdk)).resolves.toBe(true); + + expect(cache.restoreCache).toHaveBeenCalledWith( + [jdk.path], + buildJdkCacheKey(jdk) + ); + const architecturePath = path.join(jdk.path, 'x64'); + expect(fs.existsSync).toHaveBeenCalledWith(architecturePath); + expect(fs.existsSync).toHaveBeenCalledWith(`${architecturePath}.complete`); + expect(core.saveState).toHaveBeenCalledWith( + 'jdk-caches', + expect.stringContaining(buildJdkCacheKey(jdk)) + ); + }); + + it('falls back to download when restoration fails', async () => { + (cache.restoreCache as jest.Mock).mockRejectedValue( + new Error('cache unavailable') + ); + + await expect(restoreJdk(jdk)).resolves.toBe(false); + expect(core.warning).toHaveBeenCalledWith( + 'Failed to restore JDK cache: cache unavailable' + ); + }); + + it('saves a downloaded JDK registered after installation', async () => { + const jdkPath = createInstallation(); + const installed = {...jdk, path: jdkPath}; + const key = buildJdkCacheKey(installed); + (cache.restoreCache as jest.Mock).mockResolvedValue(undefined); + + await restoreJdk(installed); + registerJdk(installed); + (core.getState as jest.Mock).mockReturnValue(lastState()); + (cache.saveCache as jest.Mock).mockResolvedValue(1); + + await saveJdkCaches(); + + expect(cache.saveCache).toHaveBeenCalledWith([jdkPath], key); + }); + + it('does not save an installation that was replaced after registration', async () => { + const jdkPath = createInstallation(); + const installed = {...jdk, path: jdkPath}; + const key = buildJdkCacheKey(installed); + + registerJdk(installed); + (core.getState as jest.Mock).mockReturnValue(lastState()); + writeInstallation(jdkPath, 'replaced-by-a-later-step'); + + await saveJdkCaches(); + + expect(cache.saveCache).not.toHaveBeenCalledWith([jdkPath], key); + expect(core.warning).toHaveBeenCalledWith( + expect.stringContaining('was replaced after it was registered') + ); + }); + + it('saves only the key matching the installation that occupies the path', async () => { + const jdkPath = createInstallation(); + const verified = {...jdk, path: jdkPath, verification: 'verified:bundled'}; + const unverified = {...jdk, path: jdkPath}; + + registerJdk(verified); + writeInstallation(jdkPath, 'force-downloaded-without-verification'); + registerJdk(unverified); + (core.getState as jest.Mock).mockReturnValue(lastState()); + (cache.saveCache as jest.Mock).mockResolvedValue(1); + + await saveJdkCaches(); + + expect(cache.saveCache).not.toHaveBeenCalledWith( + [jdkPath], + buildJdkCacheKey(verified) + ); + expect(cache.saveCache).toHaveBeenCalledWith( + [jdkPath], + buildJdkCacheKey(unverified) + ); + }); + + it('does not save a path that was never registered as installed', async () => { + const jdkPath = createInstallation(); + const installed = {...jdk, path: jdkPath}; + (cache.restoreCache as jest.Mock).mockResolvedValue(undefined); + + await restoreJdk(installed); + (core.getState as jest.Mock).mockReturnValue(lastState()); + + await saveJdkCaches(); + + expect(cache.saveCache).not.toHaveBeenCalledWith( + [jdkPath], + buildJdkCacheKey(installed) + ); + }); + + it('keeps saving the remaining JDK caches when one save fails', async () => { + const failingPath = createInstallation(); + const succeedingPath = createInstallation(); + const failing = {...jdk, path: failingPath}; + const succeeding = {...jdk, path: succeedingPath, version: '17.0.19+9'}; + + registerJdk(failing); + registerJdk(succeeding); + (core.getState as jest.Mock).mockReturnValue(lastState()); + (cache.saveCache as jest.Mock).mockImplementation( + async (paths: unknown) => { + if ((paths as string[])[0] === failingPath) { + throw new Error('cache service unavailable'); + } + return 1; + } + ); + + await expect(saveJdkCaches()).resolves.toBeUndefined(); + + expect(cache.saveCache).toHaveBeenCalledWith( + [succeedingPath], + buildJdkCacheKey(succeeding) + ); + expect(core.warning).toHaveBeenCalledWith( + expect.stringContaining('cache service unavailable') + ); + expect(core.info).toHaveBeenCalledWith( + `JDK cache saved with the key: ${buildJdkCacheKey(succeeding)}` + ); + }); + + it('reports a reserved cache key without failing the remaining saves', async () => { + const reservedPath = createInstallation(); + const reserved = {...jdk, path: reservedPath}; + + registerJdk(reserved); + (core.getState as jest.Mock).mockReturnValue(lastState()); + (cache.saveCache as jest.Mock).mockRejectedValue( + new cache.ReserveCacheError('Unable to reserve cache') + ); + + await expect(saveJdkCaches()).resolves.toBeUndefined(); + + expect(core.info).toHaveBeenCalledWith('Unable to reserve cache'); + }); + + it('registers a force-downloaded JDK without restoring it', () => { + const jdkPath = createInstallation(); + registerJdk({...jdk, path: jdkPath}); + + expect(cache.restoreCache).not.toHaveBeenCalled(); + expect(core.saveState).toHaveBeenCalledWith( + 'jdk-caches', + expect.stringContaining(buildJdkCacheKey({...jdk, path: jdkPath})) + ); + }); + + it('does not save an exact JDK cache hit again', async () => { + const key = buildJdkCacheKey(jdk); + (core.getState as jest.Mock).mockReturnValue( + JSON.stringify([ + { + key, + path: jdk.path, + architecture: jdk.architecture, + matchedKey: key + } + ]) + ); + + await saveJdkCaches(); + + expect(cache.saveCache).not.toHaveBeenCalled(); + }); +}); diff --git a/__tests__/setup-java.module-loading.test.ts b/__tests__/setup-java.module-loading.test.ts index 57628927..7705abfc 100644 --- a/__tests__/setup-java.module-loading.test.ts +++ b/__tests__/setup-java.module-loading.test.ts @@ -33,7 +33,8 @@ jest.unstable_mockModule('fs', () => ({ jest.unstable_mockModule('../src/util.js', () => ({ getBooleanInput: jest.fn(), - getVersionFromFileContent: jest.fn() + getVersionFromFileContent: jest.fn(), + isJdkCacheEnabled: jest.fn() })); jest.unstable_mockModule('../src/toolchains.js', () => ({ @@ -98,6 +99,7 @@ describe('setup-java conditional module loading', () => { return booleanInputs.get(name as string) ?? defaultValue; } ); + (util.isJdkCacheEnabled as jest.Mock).mockReturnValue(false); (toolchains.configureToolchains as jest.Mock).mockResolvedValue(undefined); }); diff --git a/__tests__/setup-java.test.ts b/__tests__/setup-java.test.ts index 5538ed7d..a693d3f4 100644 --- a/__tests__/setup-java.test.ts +++ b/__tests__/setup-java.test.ts @@ -33,7 +33,8 @@ jest.unstable_mockModule('fs', () => ({ jest.unstable_mockModule('../src/util.js', () => ({ getBooleanInput: jest.fn(), - getVersionFromFileContent: jest.fn() + getVersionFromFileContent: jest.fn(), + isJdkCacheEnabled: jest.fn() })); jest.unstable_mockModule('../src/toolchains.js', () => ({ @@ -113,6 +114,14 @@ describe('setup action orchestration', () => { return booleanInputs.get(name as string) ?? defaultValue; } ); + (util.isJdkCacheEnabled as jest.Mock).mockImplementation( + (cache: string) => { + const explicit = inputs.get('cache-jdk'); + return explicit + ? (booleanInputs.get('cache-jdk') ?? explicit === 'true') + : Boolean(cache); + } + ); (cacheFeature.isCacheFeatureAvailable as jest.Mock).mockReturnValue(true); (toolchainIds.validateToolchainIds as jest.Mock).mockImplementation( () => undefined @@ -217,6 +226,7 @@ describe('setup action orchestration', () => { packageType: 'jdk', checkLatest: true, forceDownload: true, + cacheJdk: false, setDefault: false, verifySignature: true, verifySignaturePublicKey: 'public-key' @@ -457,6 +467,7 @@ describe('setup action orchestration', () => { it('does not initialize cache modules when cache input is absent', async () => { inputs.set('distribution', 'temurin'); multilineInputs.set('java-version', ['21']); + booleanInputs.set('cache-jdk', false); (factory.getJavaDistribution as jest.Mock).mockReturnValue({ setupJava: jest.fn(async () => ({ version: '21.0.4+7', @@ -468,8 +479,47 @@ describe('setup action orchestration', () => { expect(cacheFeature.isCacheFeatureAvailable).not.toHaveBeenCalled(); expect(cache.restore).not.toHaveBeenCalled(); + expect(factory.getJavaDistribution).toHaveBeenCalledWith( + 'temurin', + expect.objectContaining({cacheJdk: false}), + '' + ); }); + it.each([ + ['', '', false], + ['', 'true', true], + ['', 'false', false], + ['maven', '', true], + ['maven', 'true', true], + ['maven', 'false', false] + ])( + 'passes effective JDK caching for cache=%j and cache-jdk=%j', + async (cacheInput, cacheJdkInput, expected) => { + inputs.set('distribution', 'temurin'); + inputs.set('cache', cacheInput); + inputs.set('cache-jdk', cacheJdkInput); + multilineInputs.set('java-version', ['21']); + if (cacheJdkInput) { + booleanInputs.set('cache-jdk', cacheJdkInput === 'true'); + } + (factory.getJavaDistribution as jest.Mock).mockReturnValue({ + setupJava: jest.fn(async () => ({ + version: '21.0.4+7', + path: '/opt/java/21' + })) + }); + + await run(); + + expect(factory.getJavaDistribution).toHaveBeenCalledWith( + 'temurin', + expect.objectContaining({cacheJdk: expected}), + '' + ); + } + ); + it('reports unsupported distributions through core.setFailed', async () => { inputs.set('distribution', 'unsupported'); multilineInputs.set('java-version', ['21']); diff --git a/__tests__/util.test.ts b/__tests__/util.test.ts index 00481087..05bf6deb 100644 --- a/__tests__/util.test.ts +++ b/__tests__/util.test.ts @@ -49,7 +49,8 @@ const { isGhes, validatePaginationUrl, getLatestMajorVersion, - getBooleanInput + getBooleanInput, + isJdkCacheEnabled } = await import('../src/util.js'); describe('getBooleanInput', () => { @@ -115,6 +116,37 @@ describe('getBooleanInput', () => { }); }); +describe('isJdkCacheEnabled', () => { + let inputs: Record; + + beforeEach(() => { + inputs = {}; + (core.getInput as jest.Mock).mockImplementation( + (name: string) => inputs[name] ?? '' + ); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + it.each([ + ['', '', false], + ['', 'true', true], + ['', 'false', false], + ['maven', '', true], + ['maven', 'true', true], + ['maven', 'false', false] + ])( + 'resolves cache=%j and cache-jdk=%j to %s', + (cache, cacheJdk, expected) => { + inputs['cache-jdk'] = cacheJdk; + + expect(isJdkCacheEnabled(cache)).toBe(expected); + } + ); +}); + describe('isVersionSatisfies', () => { it.each([ ['x', '11.0.0', true], diff --git a/action.yml b/action.yml index 280f83c1..6146f2c9 100644 --- a/action.yml +++ b/action.yml @@ -84,6 +84,9 @@ inputs: cache: description: 'Name of the build platform to cache dependencies. It can be "maven", "gradle" or "sbt".' required: false + cache-jdk: + description: 'Cache downloaded JDK installations between jobs. Defaults to enabled when dependency caching is configured with `cache`; set explicitly to "true" or "false" to override.' + 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 @@ -91,7 +94,7 @@ inputs: 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.' + description: 'Restore caches without saving cache changes in the post action.' required: false default: false job-status: diff --git a/dist/cleanup/314.index.js b/dist/cleanup/314.index.js new file mode 100644 index 00000000..1d80a136 --- /dev/null +++ b/dist/cleanup/314.index.js @@ -0,0 +1,224 @@ +export const id = 314; +export const ids = [314]; +export const modules = { + +/***/ 2314: +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + + +// EXPORTS +__webpack_require__.d(__webpack_exports__, { + saveJdkCaches: () => (/* binding */ saveJdkCaches) +}); + +// UNUSED EXPORTS: buildJdkCacheKey, getJdkVerificationIdentity, registerJdk, restoreJdk + +// EXTERNAL MODULE: external "crypto" +var external_crypto_ = __webpack_require__(6982); +// EXTERNAL MODULE: external "fs" +var external_fs_ = __webpack_require__(9896); +var external_fs_default = /*#__PURE__*/__webpack_require__.n(external_fs_); +// EXTERNAL MODULE: external "path" +var external_path_ = __webpack_require__(6928); +var external_path_default = /*#__PURE__*/__webpack_require__.n(external_path_); +// EXTERNAL MODULE: ./node_modules/@actions/cache/lib/cache.js + 291 modules +var lib_cache = __webpack_require__(5767); +// EXTERNAL MODULE: ./node_modules/@actions/core/lib/core.js + 7 modules +var lib_core = __webpack_require__(3838); +// EXTERNAL MODULE: ./src/util.ts +var util = __webpack_require__(4527); +;// CONCATENATED MODULE: ./src/cache-feature.ts + + + +function cache_feature_isCacheFeatureAvailable() { + if (cache.isFeatureAvailable()) { + return true; + } + if (isGhes()) { + core.warning('Caching is only supported on GHES version >= 3.5. If you are on a version >= 3.5, please check with your GHES admin if the Actions cache service is enabled or not.'); + return false; + } + core.warning('The runner was not able to contact the cache service. Caching will be skipped'); + return false; +} + +;// CONCATENATED MODULE: ./src/jdk-cache.ts + + + + + + +const STATE_JDK_CACHES = 'jdk-caches'; +const JDK_CACHE_KEY_VERSION = 1; +const restoredCaches = (/* unused pure expression or super */ null && ([])); +async function restoreJdk(jdk) { + if (!jdk.path || !isCacheFeatureAvailable()) { + return false; + } + const key = buildJdkCacheKey(jdk); + let matchedKey; + try { + matchedKey = await cache.restoreCache([jdk.path], key); + } + catch (error) { + core.warning(`Failed to restore JDK cache: ${error.message}`); + } + const architecturePath = path.join(jdk.path, jdk.architecture); + if (matchedKey && + (!fs.existsSync(architecturePath) || + !fs.existsSync(`${architecturePath}.complete`))) { + core.warning(`JDK cache key ${matchedKey} was restored without the expected tool-cache path; downloading the JDK instead.`); + matchedKey = undefined; + } + recordJdkCache({ + key, + path: jdk.path, + architecture: jdk.architecture, + matchedKey + }); + if (matchedKey) { + core.info(`JDK cache restored from key: ${matchedKey}`); + return true; + } + core.info(`JDK cache is not found for ${jdk.distribution} ${jdk.version}`); + return false; +} +function registerJdk(jdk) { + if (!jdk.path) { + return; + } + recordJdkCache({ + key: buildJdkCacheKey(jdk), + path: jdk.path, + architecture: jdk.architecture, + installation: getInstallationIdentity(jdk.path, jdk.architecture) + }); +} +/** + * Cheap fingerprint of the installation stored at a tool-cache path. The + * `.complete` marker is (re)created by `tc.cacheDir` every time an + * installation is written, so its inode and timestamps change whenever the + * installation is replaced. This avoids rehashing a multi-hundred-megabyte JDK + * directory while still detecting that the bytes behind a key were swapped. + */ +function getInstallationIdentity(jdkPath, architecture) { + const architecturePath = external_path_default().join(jdkPath, architecture); + try { + const marker = external_fs_default().statSync(`${architecturePath}.complete`); + const installation = external_fs_default().statSync(architecturePath); + return [ + marker.ino, + marker.mtimeMs, + marker.ctimeMs, + marker.size, + installation.ino, + installation.mtimeMs, + installation.ctimeMs + ].join(':'); + } + catch { + return undefined; + } +} +function getJdkVerificationIdentity(verifySignature, publicKey) { + if (!verifySignature) { + return 'unverified'; + } + if (!publicKey) { + return 'verified:bundled'; + } + const normalizedKey = publicKey.replace(/\r\n?/g, '\n').trim(); + const fingerprint = createHash('sha256').update(normalizedKey).digest('hex'); + return `verified:custom:sha256:${fingerprint}`; +} +async function saveJdkCaches() { + const state = lib_core/* getState */.Gu(STATE_JDK_CACHES); + if (!state) { + return; + } + const caches = parseJdkCacheState(state); + for (const jdk of caches) { + if (jdk.matchedKey === jdk.key) { + lib_core/* info */.pq(`Cache hit occurred on the JDK primary key ${jdk.key}, not saving cache.`); + continue; + } + if (!external_fs_default().existsSync(jdk.path)) { + lib_core/* debug */.Yz(`JDK cache path does not exist, not saving: ${jdk.path}`); + continue; + } + if (!jdk.installation) { + lib_core/* debug */.Yz(`No JDK installation was registered for the key ${jdk.key}, not saving cache.`); + continue; + } + if (getInstallationIdentity(jdk.path, jdk.architecture) !== jdk.installation) { + lib_core/* warning */.$e(`The JDK installation in ${jdk.path} was replaced after it was registered for the key ${jdk.key}; not saving cache.`); + continue; + } + try { + const cacheId = await lib_cache/* saveCache */.Io([jdk.path], jdk.key); + if (cacheId !== -1) { + lib_core/* info */.pq(`JDK cache saved with the key: ${jdk.key}`); + } + } + catch (error) { + const err = error; + if (err.name === lib_cache/* ReserveCacheError */.Zh.name) { + lib_core/* info */.pq(err.message); + } + else { + // Saving is best-effort and per entry: one failure must not suppress + // the remaining JDK caches. + lib_core/* warning */.$e(`Failed to save the JDK cache with the key ${jdk.key}: ${err.message}`); + } + } + } +} +function buildJdkCacheKey(jdk) { + const runnerOs = process.env['RUNNER_OS'] ?? process.platform; + const normalizedArchitecture = jdk.architecture.toLowerCase(); + const identity = JSON.stringify({ + keyVersion: JDK_CACHE_KEY_VERSION, + runnerOs, + distribution: jdk.distribution.toLowerCase(), + packageType: jdk.packageType.toLowerCase(), + architecture: normalizedArchitecture, + version: jdk.version, + source: jdk.source, + verification: jdk.verification + }); + const digest = createHash('sha256').update(identity).digest('hex'); + return `setup-java-jdk-v${JDK_CACHE_KEY_VERSION}-${runnerOs}-${normalizedArchitecture}-${digest}`; +} +function recordJdkCache(jdk) { + const existing = restoredCaches.findIndex(item => item.key === jdk.key && item.path === jdk.path); + if (existing === -1) { + restoredCaches.push(jdk); + } + else { + restoredCaches[existing] = { ...restoredCaches[existing], ...jdk }; + } + core.saveState(STATE_JDK_CACHES, JSON.stringify(restoredCaches)); +} +function parseJdkCacheState(state) { + const value = JSON.parse(state); + if (!Array.isArray(value) || + !value.every(item => typeof item === 'object' && + item !== null && + typeof item.key === 'string' && + typeof item.path === 'string' && + typeof item.architecture === 'string' && + (item.matchedKey === undefined || + typeof item.matchedKey === 'string') && + (item.installation === undefined || + typeof item.installation === 'string'))) { + throw new Error('Invalid JDK cache information retrieved from state.'); + } + return value; +} + + +/***/ }) + +}; diff --git a/dist/cleanup/index.js b/dist/cleanup/index.js index 6624703b..5770ae79 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -30762,6 +30762,407 @@ module.exports = { } +/***/ }), + +/***/ 7242: +/***/ ((__unused_webpack_module, __webpack_exports__, __nccwpck_require__) => { + +/* harmony export */ __nccwpck_require__.d(__webpack_exports__, { +/* harmony export */ Ch: () => (/* binding */ INPUT_CACHE_READ_ONLY), +/* harmony export */ GL: () => (/* binding */ INPUT_CACHE_JDK), +/* harmony export */ gk: () => (/* binding */ INPUT_CACHE), +/* harmony export */ wG: () => (/* binding */ INPUT_JOB_STATUS), +/* harmony export */ wm: () => (/* binding */ STATE_GPG_PRIVATE_KEY_FINGERPRINT), +/* harmony export */ wz: () => (/* binding */ INPUT_GPG_PRIVATE_KEY) +/* harmony export */ }); +/* unused harmony exports MACOS_JAVA_CONTENT_POSTFIX, INPUT_JAVA_VERSION, INPUT_JAVA_VERSION_FILE, INPUT_ARCHITECTURE, INPUT_JAVA_PACKAGE, INPUT_DISTRIBUTION, INPUT_JDK_FILE, INPUT_JDK_FILE_DEPRECATED, INPUT_CHECK_LATEST, INPUT_FORCE_DOWNLOAD, INPUT_SET_DEFAULT, INPUT_PROBLEM_MATCHER, INPUT_VERIFY_SIGNATURE, INPUT_VERIFY_SIGNATURE_PUBLIC_KEY, INPUT_SERVER_ID, INPUT_SERVER_USERNAME_ENV_VAR, INPUT_SERVER_PASSWORD_ENV_VAR, INPUT_SERVER_USERNAME_DEPRECATED, INPUT_SERVER_PASSWORD_DEPRECATED, INPUT_SETTINGS_PATH, INPUT_OVERWRITE_SETTINGS, INPUT_GPG_PASSPHRASE_ENV_VAR, INPUT_GPG_PASSPHRASE_DEPRECATED, INPUT_DEFAULT_SERVER_USERNAME, INPUT_DEFAULT_SERVER_PASSWORD, INPUT_DEFAULT_GPG_PRIVATE_KEY, INPUT_DEFAULT_GPG_PASSPHRASE, MAVEN_GPG_PASSPHRASE_DEFAULT_ENV, GPG_PASSPHRASE_PROFILE_ID, INPUT_CACHE_DEPENDENCY_PATH, INPUT_CACHE_PATH, M2_DIR, MVN_SETTINGS_FILE, MVN_TOOLCHAINS_FILE, INPUT_MVN_TOOLCHAIN_ID, INPUT_MVN_TOOLCHAIN_VENDOR, INPUT_SHOW_DOWNLOAD_PROGRESS, MAVEN_ARGS_ENV, MAVEN_NO_TRANSFER_PROGRESS_FLAG, MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG, DISTRIBUTIONS_ONLY_MAJOR_VERSION */ +const MACOS_JAVA_CONTENT_POSTFIX = 'Contents/Home'; +const INPUT_JAVA_VERSION = 'java-version'; +const INPUT_JAVA_VERSION_FILE = 'java-version-file'; +const INPUT_ARCHITECTURE = 'architecture'; +const INPUT_JAVA_PACKAGE = 'java-package'; +const INPUT_DISTRIBUTION = 'distribution'; +const INPUT_JDK_FILE = 'jdk-file'; +const INPUT_JDK_FILE_DEPRECATED = 'jdkFile'; +const INPUT_CHECK_LATEST = 'check-latest'; +const INPUT_FORCE_DOWNLOAD = 'force-download'; +const INPUT_SET_DEFAULT = 'set-default'; +const INPUT_PROBLEM_MATCHER = 'problem-matcher'; +const INPUT_VERIFY_SIGNATURE = 'verify-signature'; +const INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = 'verify-signature-public-key'; +const INPUT_SERVER_ID = 'server-id'; +const INPUT_SERVER_USERNAME_ENV_VAR = 'server-username-env-var'; +const INPUT_SERVER_PASSWORD_ENV_VAR = 'server-password-env-var'; +const INPUT_SERVER_USERNAME_DEPRECATED = 'server-username'; +const INPUT_SERVER_PASSWORD_DEPRECATED = 'server-password'; +const INPUT_SETTINGS_PATH = 'settings-path'; +const INPUT_OVERWRITE_SETTINGS = 'overwrite-settings'; +const INPUT_GPG_PRIVATE_KEY = 'gpg-private-key'; +const INPUT_GPG_PASSPHRASE_ENV_VAR = 'gpg-passphrase-env-var'; +const INPUT_GPG_PASSPHRASE_DEPRECATED = 'gpg-passphrase'; +const INPUT_DEFAULT_SERVER_USERNAME = 'GITHUB_ACTOR'; +const INPUT_DEFAULT_SERVER_PASSWORD = 'GITHUB_TOKEN'; +const INPUT_DEFAULT_GPG_PRIVATE_KEY = (/* unused pure expression or super */ null && (undefined)); +const INPUT_DEFAULT_GPG_PASSPHRASE = 'GPG_PASSPHRASE'; +// The default name of the environment variable the maven-gpg-plugin reads the +// passphrase from (property `gpg.passphraseEnvName`). When the configured +// passphrase env var name matches this, no extra configuration is required. +const MAVEN_GPG_PASSPHRASE_DEFAULT_ENV = 'MAVEN_GPG_PASSPHRASE'; +// Id of the settings.xml profile used to set `gpg.passphraseEnvName`. +const GPG_PASSPHRASE_PROFILE_ID = 'setup-java-gpg'; +const INPUT_CACHE = 'cache'; +const INPUT_CACHE_JDK = 'cache-jdk'; +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'; +const M2_DIR = '.m2'; +const MVN_SETTINGS_FILE = 'settings.xml'; +const MVN_TOOLCHAINS_FILE = 'toolchains.xml'; +const INPUT_MVN_TOOLCHAIN_ID = 'mvn-toolchain-id'; +const INPUT_MVN_TOOLCHAIN_VENDOR = 'mvn-toolchain-vendor'; +const INPUT_SHOW_DOWNLOAD_PROGRESS = 'show-download-progress'; +const MAVEN_ARGS_ENV = 'MAVEN_ARGS'; +const MAVEN_NO_TRANSFER_PROGRESS_FLAG = '-ntp'; +const MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG = '--no-transfer-progress'; +const DISTRIBUTIONS_ONLY_MAJOR_VERSION = (/* unused pure expression or super */ null && (['corretto'])); + + +/***/ }), + +/***/ 4527: +/***/ ((__unused_webpack_module, __webpack_exports__, __nccwpck_require__) => { + +/* harmony export */ __nccwpck_require__.d(__webpack_exports__, { +/* harmony export */ G4: () => (/* binding */ getTempDir), +/* harmony export */ TX: () => (/* binding */ isJobStatusSuccess), +/* harmony export */ Vt: () => (/* binding */ getBooleanInput), +/* harmony export */ lN: () => (/* binding */ isJdkCacheEnabled) +/* harmony export */ }); +/* unused harmony exports getVersionFromToolcachePath, extractJdkFile, getDownloadArchiveExtension, isVersionSatisfies, getToolcachePath, isGhes, getVersionFromFileContent, convertVersionToSemver, getGitHubHttpHeaders, MAX_PAGINATION_PAGES, getNextPageUrlFromLinkHeader, validatePaginationUrl, renameWinArchive, getLatestMajorVersion */ +/* harmony import */ var os__WEBPACK_IMPORTED_MODULE_0__ = __nccwpck_require__(857); +/* harmony import */ var os__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__nccwpck_require__.n(os__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1__ = __nccwpck_require__(6928); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__nccwpck_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_2__ = __nccwpck_require__(9896); +/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__nccwpck_require__.n(fs__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var semver__WEBPACK_IMPORTED_MODULE_3__ = __nccwpck_require__(2088); +/* harmony import */ var semver__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__nccwpck_require__.n(semver__WEBPACK_IMPORTED_MODULE_3__); +/* harmony import */ var _actions_core__WEBPACK_IMPORTED_MODULE_4__ = __nccwpck_require__(3838); +/* harmony import */ var _actions_tool_cache__WEBPACK_IMPORTED_MODULE_5__ = __nccwpck_require__(9805); +/* harmony import */ var _constants_js__WEBPACK_IMPORTED_MODULE_6__ = __nccwpck_require__(7242); + + + + + + + +function getTempDir() { + const tempDirectory = process.env['RUNNER_TEMP'] || os__WEBPACK_IMPORTED_MODULE_0___default().tmpdir(); + return tempDirectory; +} +function getBooleanInput(inputName, defaultValue = false) { + const inputValue = _actions_core__WEBPACK_IMPORTED_MODULE_4__/* .getInput */ .V4(inputName); + const normalizedValue = inputValue.trim().toLowerCase(); + if (!normalizedValue) { + return defaultValue; + } + if (normalizedValue === 'true') { + return true; + } + if (normalizedValue === 'false') { + return false; + } + throw new Error(`Invalid value '${inputValue}' for boolean input '${inputName}'. Expected 'true' or 'false'.`); +} +function isJdkCacheEnabled(cache) { + return _actions_core__WEBPACK_IMPORTED_MODULE_4__/* .getInput */ .V4(_constants_js__WEBPACK_IMPORTED_MODULE_6__/* .INPUT_CACHE_JDK */ .GL).trim() + ? getBooleanInput(_constants_js__WEBPACK_IMPORTED_MODULE_6__/* .INPUT_CACHE_JDK */ .GL) + : Boolean(cache.trim()); +} +function getVersionFromToolcachePath(toolPath) { + if (toolPath) { + return path.basename(path.dirname(toolPath)); + } + return toolPath; +} +async function extractJdkFile(toolPath, extension) { + if (!extension) { + extension = toolPath.endsWith('.tar.gz') + ? 'tar.gz' + : path.extname(toolPath); + if (extension.startsWith('.')) { + extension = extension.substring(1); + } + } + switch (extension) { + case 'tar.gz': + case 'tar': + return await tc.extractTar(toolPath); + case 'zip': + return await tc.extractZip(toolPath); + default: + return await tc.extract7z(toolPath); + } +} +function getDownloadArchiveExtension() { + return process.platform === 'win32' ? 'zip' : 'tar.gz'; +} +function isVersionSatisfies(range, version) { + // Some distributions (e.g. JetBrains Runtime) publish 4-segment versions + // like '17.0.8.1+1080.1' that semver rejects. If the candidate version + // isn't valid semver, it can't match — bail out rather than letting + // compareBuild / satisfies throw. + if (!semver.valid(version)) { + return false; + } + if (semver.valid(range)) { + // if full version with build digit is provided as a range (such as '1.2.3+4') + // we should check for exact equal via compareBuild + // since semver.satisfies doesn't handle 4th digit + const semRange = semver.parse(range); + if (semRange && semRange.build?.length > 0) { + return semver.compareBuild(range, version) === 0; + } + } + return semver.satisfies(version, range); +} +function getToolcachePath(toolName, version, architecture) { + const toolcacheRoot = process.env['RUNNER_TOOL_CACHE'] ?? ''; + const fullPath = path.join(toolcacheRoot, toolName, version, architecture); + if (fs.existsSync(fullPath)) { + return fullPath; + } + return null; +} +function isJobStatusSuccess() { + const jobStatus = _actions_core__WEBPACK_IMPORTED_MODULE_4__/* .getInput */ .V4(_constants_js__WEBPACK_IMPORTED_MODULE_6__/* .INPUT_JOB_STATUS */ .wG); + return jobStatus === 'success'; +} +function isGhes() { + const ghUrl = new URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com'); + const hostname = ghUrl.hostname.trimEnd().toUpperCase(); + const isGitHubHost = hostname === 'GITHUB.COM'; + const isGitHubEnterpriseCloudHost = hostname.endsWith('.GHE.COM'); + const isLocalHost = hostname.endsWith('.LOCALHOST'); + return !isGitHubHost && !isGitHubEnterpriseCloudHost && !isLocalHost; +} +function getVersionFromFileContent(content, distributionName, versionFile) { + let javaVersionRegExp; + let extractedDistribution; + function getFileName(versionFile) { + return path.basename(versionFile); + } + const versionFileName = getFileName(versionFile); + if (versionFileName == '.tool-versions') { + // Capture an optional asdf-java vendor prefix (e.g. `temurin-`, `corretto-`) + // in the `distribution` group so it can be mapped to a setup-java distribution. + javaVersionRegExp = + /^java\s+(?:(?\S*)-)?(?\d+(?:\.\d+)*([+_.-](?:openj9[-._]?\d[\w.-]*|java\d+|jre[-_\w]*|OpenJDK\d+[\w_.-]*|[a-z0-9]+))*)/im; + } + else if (versionFileName == '.sdkmanrc') { + // Match both version and optional distribution identifier + javaVersionRegExp = + /^java\s*=\s*(?[^-\s]+)(?:-(?[a-z0-9]+))?/m; + } + else { + javaVersionRegExp = /(?(?<=(^|\s|-))(\d+\S*))(\s|$)/; + } + const match = content.match(javaVersionRegExp); + const capturedVersion = match?.groups?.version + ? match.groups.version + : ''; + // Extract distribution from .sdkmanrc file + if (versionFileName == '.sdkmanrc' && match?.groups?.distribution) { + const sdkmanDist = match.groups.distribution; + extractedDistribution = mapSdkmanDistribution(sdkmanDist); + core.debug(`Parsed distribution '${extractedDistribution}' from SDKMAN identifier '${sdkmanDist}'`); + } + // Extract distribution from asdf .tool-versions file + if (versionFileName == '.tool-versions' && match?.groups?.distribution) { + const asdfDist = match.groups.distribution; + extractedDistribution = mapAsdfDistribution(asdfDist); + if (extractedDistribution) { + core.debug(`Parsed distribution '${extractedDistribution}' from asdf identifier '${asdfDist}'`); + } + } + core.debug(`Parsed version '${capturedVersion}' from file '${versionFileName}'`); + if (!capturedVersion) { + return null; + } + const tentativeVersion = avoidOldNotation(capturedVersion); + const rawVersion = tentativeVersion.split('-')[0]; + let version = semver.validRange(rawVersion) + ? tentativeVersion + : semver.coerce(tentativeVersion); + core.debug(`Range version from file is '${version}'`); + if (!version) { + return null; + } + // Apply DISTRIBUTIONS_ONLY_MAJOR_VERSION logic whenever the effective distribution + // (either explicitly provided or extracted from the version file) is in the list. + if (DISTRIBUTIONS_ONLY_MAJOR_VERSION.includes(extractedDistribution || distributionName)) { + const coerceVersion = semver.coerce(version) ?? version; + version = semver.major(coerceVersion).toString(); + } + return { + version: version.toString(), + distribution: extractedDistribution + }; +} +// Map SDKMAN distribution identifiers to setup-java distribution names +function mapSdkmanDistribution(sdkmanDist) { + const distributionMap = { + tem: 'temurin', + sem: 'semeru', + albba: 'dragonwell', + zulu: 'zulu', + amzn: 'corretto', + graal: 'graalvm', + graalce: 'graalvm', + librca: 'liberica', + ms: 'microsoft', + oracle: 'oracle', + sapmchn: 'sapmachine', + jbr: 'jetbrains', + dragonwell: 'dragonwell', + kona: 'kona' + }; + const mapped = distributionMap[sdkmanDist.toLowerCase()]; + if (!mapped) { + core.warning(`Unknown SDKMAN distribution identifier '${sdkmanDist}'. Please specify the distribution explicitly.`); + } + return mapped; +} +// Map asdf-java (.tool-versions) vendor identifiers to setup-java distribution names. +// asdf-java encodes the vendor as a prefix on the version string, e.g. +// `java temurin-17.0.3+7` or `java semeru-openj9-11.0.25+9`. Packaging variants +// (`-jre`, `-musl`, `-openj9`, `-crac`, `-javafx`, ...) are collapsed onto the +// base vendor since setup-java does not distinguish them here. +function mapAsdfDistribution(asdfDist) { + const normalized = asdfDist.toLowerCase(); + // Multi-segment vendors that map to a distinct setup-java distribution. + if (normalized.startsWith('graalvm-community')) { + return 'graalvm-community'; + } + if (normalized.startsWith('oracle-graalvm')) { + return 'graalvm'; + } + const baseVendor = normalized.split('-')[0]; + const distributionMap = { + temurin: 'temurin', + adoptopenjdk: 'temurin', + zulu: 'zulu', + corretto: 'corretto', + liberica: 'liberica', + microsoft: 'microsoft', + semeru: 'semeru', + ibm: 'semeru', + dragonwell: 'dragonwell', + graalvm: 'graalvm', + oracle: 'oracle', + sapmachine: 'sapmachine', + kona: 'kona', + jetbrains: 'jetbrains' + }; + const mapped = distributionMap[baseVendor]; + if (!mapped) { + core.warning(`Unknown asdf distribution identifier '${asdfDist}'. Please specify the distribution explicitly.`); + } + return mapped; +} +// By convention, action expects version 8 in the format `8.*` instead of `1.8` +function avoidOldNotation(content) { + return content.startsWith('1.') ? content.substring(2) : content; +} +function convertVersionToSemver(version) { + // Some distributions may use semver-like notation (12.10.2.1, 12.10.2.1.1) + const versionArray = Array.isArray(version) ? version : version.split('.'); + const mainVersion = versionArray.slice(0, 3).join('.'); + if (versionArray.length > 3) { + return `${mainVersion}+${versionArray.slice(3).join('.')}`; + } + return mainVersion; +} +function getGitHubHttpHeaders() { + const resolvedToken = core.getInput('token') || process.env.GITHUB_TOKEN; + const auth = !resolvedToken ? undefined : `token ${resolvedToken}`; + const headers = { + accept: 'application/vnd.github.VERSION.raw' + }; + if (auth) { + headers.authorization = auth; + } + return headers; +} +const MAX_PAGINATION_PAGES = 1000; +function getNextPageUrlFromLinkHeader(headers) { + if (!headers) { + return null; + } + const linkHeader = headers.link ?? headers.Link; + if (!linkHeader) { + return null; + } + const normalizedLinkHeader = Array.isArray(linkHeader) + ? linkHeader.join(',') + : linkHeader; + // Split into individual link-values and find the one with rel="next" + // RFC 8288 allows rel to appear anywhere among the parameters + const linkValues = normalizedLinkHeader.split(/,(?=\s*<)/); + for (const linkValue of linkValues) { + const urlMatch = linkValue.match(/<([^>]+)>/); + if (!urlMatch) + continue; + const params = linkValue.slice(urlMatch[0].length); + // Use word boundary to match "next" as a standalone relation type + // RFC 8288 allows space-separated relation types like rel="next prev" + if (/;\s*rel="?[^"]*\bnext\b/i.test(params)) { + return urlMatch[1]; + } + } + return null; +} +function validatePaginationUrl(url, allowedOrigin) { + try { + const parsed = new URL(url); + const allowed = new URL(allowedOrigin); + return parsed.origin === allowed.origin; + } + catch { + return false; + } +} +// Rename archive to add extension because after downloading +// archive does not contain extension type and it leads to some issues +// on Windows runners without PowerShell Core. +// +// For default PowerShell Windows it should contain extension type to unpack it. +function renameWinArchive(javaArchivePath) { + const javaArchivePathRenamed = `${javaArchivePath}.zip`; + fs.renameSync(javaArchivePath, javaArchivePathRenamed); + return javaArchivePathRenamed; +} +// Resolve the newest available stable/GA feature (major) release. +// +// Some distributions (e.g. Oracle, GraalVM) construct their download URLs from a +// concrete major version and don't expose an endpoint to list every available +// release, so a bare `latest` alias can't be resolved from their own metadata. +// The Adoptium (Temurin) API is used as a proxy for "what is the newest GA major +// version out there", which those distributions typically publish at the same time. +async function getLatestMajorVersion(http) { + const availableReleasesUrl = 'https://api.adoptium.net/v3/info/available_releases'; + const response = await http.getJson(availableReleasesUrl); + const mostRecent = response.result?.most_recent_feature_release; + if (!mostRecent || Number.isNaN(Number(mostRecent))) { + throw new Error(`Could not determine the latest available Java major version from ${availableReleasesUrl}`); + } + return Number(mostRecent); +} + + /***/ }), /***/ 2613: @@ -34111,228 +34512,26 @@ function copyFile(srcFile, destFile, force) { } //# sourceMappingURL=io.js.map -/***/ }) +/***/ }), -/******/ }); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __nccwpck_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ var threw = true; -/******/ try { -/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __nccwpck_require__); -/******/ threw = false; -/******/ } finally { -/******/ if(threw) delete __webpack_module_cache__[moduleId]; -/******/ } -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __nccwpck_require__.m = __webpack_modules__; -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/compat get default export */ -/******/ (() => { -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __nccwpck_require__.n = (module) => { -/******/ var getter = module && module.__esModule ? -/******/ () => (module['default']) : -/******/ () => (module); -/******/ __nccwpck_require__.d(getter, { a: getter }); -/******/ return getter; -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/create fake namespace object */ -/******/ (() => { -/******/ var getProto = Object.getPrototypeOf ? (obj) => (Object.getPrototypeOf(obj)) : (obj) => (obj.__proto__); -/******/ var leafPrototypes; -/******/ // create a fake namespace object -/******/ // mode & 1: value is a module id, require it -/******/ // mode & 2: merge all properties of value into the ns -/******/ // mode & 4: return value when already ns object -/******/ // mode & 16: return value when it's Promise-like -/******/ // mode & 8|1: behave like require -/******/ __nccwpck_require__.t = function(value, mode) { -/******/ if(mode & 1) value = this(value); -/******/ if(mode & 8) return value; -/******/ if(typeof value === 'object' && value) { -/******/ if((mode & 4) && value.__esModule) return value; -/******/ if((mode & 16) && typeof value.then === 'function') return value; -/******/ } -/******/ var ns = Object.create(null); -/******/ __nccwpck_require__.r(ns); -/******/ var def = {}; -/******/ leafPrototypes = leafPrototypes || [null, getProto({}), getProto([]), getProto(getProto)]; -/******/ for(var current = mode & 2 && value; typeof current == 'object' && !~leafPrototypes.indexOf(current); current = getProto(current)) { -/******/ Object.getOwnPropertyNames(current).forEach((key) => (def[key] = () => (value[key]))); -/******/ } -/******/ def['default'] = () => (value); -/******/ __nccwpck_require__.d(ns, def); -/******/ return ns; -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/define property getters */ -/******/ (() => { -/******/ // define getter functions for harmony exports -/******/ __nccwpck_require__.d = (exports, definition) => { -/******/ for(var key in definition) { -/******/ if(__nccwpck_require__.o(definition, key) && !__nccwpck_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } -/******/ } -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/ensure chunk */ -/******/ (() => { -/******/ __nccwpck_require__.f = {}; -/******/ // This file contains only the entry chunk. -/******/ // The chunk loading function for additional chunks -/******/ __nccwpck_require__.e = (chunkId) => { -/******/ return Promise.all(Object.keys(__nccwpck_require__.f).reduce((promises, key) => { -/******/ __nccwpck_require__.f[key](chunkId, promises); -/******/ return promises; -/******/ }, [])); -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/get javascript chunk filename */ -/******/ (() => { -/******/ // This function allow to reference async chunks -/******/ __nccwpck_require__.u = (chunkId) => { -/******/ // return url for filenames based on template -/******/ return "" + chunkId + ".index.js"; -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __nccwpck_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __nccwpck_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/compat */ -/******/ -/******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = new URL('.', import.meta.url).pathname.slice(import.meta.url.match(/^file:\/\/\/\w:/) ? 1 : 0, -1) + "/"; -/******/ -/******/ /* webpack/runtime/import chunk loading */ -/******/ (() => { -/******/ // no baseURI -/******/ -/******/ // object to store loaded and loading chunks -/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched -/******/ // [resolve, Promise] = chunk loading, 0 = chunk loaded -/******/ var installedChunks = { -/******/ 792: 0 -/******/ }; -/******/ -/******/ var installChunk = (data) => { -/******/ var {ids, modules, runtime} = data; -/******/ // add "modules" to the modules object, -/******/ // then flag all "ids" as loaded and fire callback -/******/ var moduleId, chunkId, i = 0; -/******/ for(moduleId in modules) { -/******/ if(__nccwpck_require__.o(modules, moduleId)) { -/******/ __nccwpck_require__.m[moduleId] = modules[moduleId]; -/******/ } -/******/ } -/******/ if(runtime) runtime(__nccwpck_require__); -/******/ for(;i < ids.length; i++) { -/******/ chunkId = ids[i]; -/******/ if(__nccwpck_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) { -/******/ installedChunks[chunkId][0](); -/******/ } -/******/ installedChunks[ids[i]] = 0; -/******/ } -/******/ -/******/ } -/******/ -/******/ __nccwpck_require__.f.j = (chunkId, promises) => { -/******/ // import() chunk loading for javascript -/******/ var installedChunkData = __nccwpck_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined; -/******/ if(installedChunkData !== 0) { // 0 means "already installed". -/******/ -/******/ // a Promise means "currently loading". -/******/ if(installedChunkData) { -/******/ promises.push(installedChunkData[1]); -/******/ } else { -/******/ if(true) { // all chunks have JS -/******/ // setup Promise in chunk cache -/******/ var promise = import("./" + __nccwpck_require__.u(chunkId)).then(installChunk, (e) => { -/******/ if(installedChunks[chunkId] !== 0) installedChunks[chunkId] = undefined; -/******/ throw e; -/******/ }); -/******/ var promise = Promise.race([promise, new Promise((resolve) => (installedChunkData = installedChunks[chunkId] = [resolve]))]) -/******/ promises.push(installedChunkData[1] = promise); -/******/ } -/******/ } -/******/ } -/******/ }; -/******/ -/******/ // no prefetching -/******/ -/******/ // no preloaded -/******/ -/******/ // no external install chunk -/******/ -/******/ // no on chunks loaded -/******/ })(); -/******/ -/************************************************************************/ -var __webpack_exports__ = {}; +/***/ 9805: +/***/ ((__unused_webpack___webpack_module__, __unused_webpack___webpack_exports__, __nccwpck_require__) => { -// EXPORTS -__nccwpck_require__.d(__webpack_exports__, { - e: () => (/* binding */ run) -}); + +// UNUSED EXPORTS: HTTPError, cacheDir, cacheFile, downloadTool, evaluateVersions, extract7z, extractTar, extractXar, extractZip, find, findAllVersions, findFromManifest, getManifestFromRepo, isExplicitVersion // EXTERNAL MODULE: ./node_modules/@actions/core/lib/core.js + 7 modules var lib_core = __nccwpck_require__(3838); -// EXTERNAL MODULE: external "fs" -var external_fs_ = __nccwpck_require__(9896); -// EXTERNAL MODULE: external "path" -var external_path_ = __nccwpck_require__(6928); // EXTERNAL MODULE: ./node_modules/@actions/io/lib/io.js var lib_io = __nccwpck_require__(8701); -// EXTERNAL MODULE: ./node_modules/@actions/exec/lib/exec.js + 2 modules -var lib_exec = __nccwpck_require__(5260); // EXTERNAL MODULE: external "crypto" var external_crypto_ = __nccwpck_require__(6982); +// EXTERNAL MODULE: external "fs" +var external_fs_ = __nccwpck_require__(9896); // EXTERNAL MODULE: ./node_modules/semver/index.js var node_modules_semver = __nccwpck_require__(2088); // EXTERNAL MODULE: external "os" var external_os_ = __nccwpck_require__(857); -var external_os_default = /*#__PURE__*/__nccwpck_require__.n(external_os_); // EXTERNAL MODULE: external "child_process" var external_child_process_ = __nccwpck_require__(5317); ;// CONCATENATED MODULE: ./node_modules/@actions/tool-cache/lib/manifest.js @@ -34441,6 +34640,8 @@ function _readLinuxVersionFile() { return _internal.readLinuxVersionFile(); } //# sourceMappingURL=manifest.js.map +// EXTERNAL MODULE: external "path" +var external_path_ = __nccwpck_require__(6928); // EXTERNAL MODULE: ./node_modules/@actions/http-client/lib/index.js + 1 modules var lib = __nccwpck_require__(4942); // EXTERNAL MODULE: external "stream" @@ -34449,6 +34650,8 @@ var external_stream_ = __nccwpck_require__(2203); var external_util_ = __nccwpck_require__(9023); // EXTERNAL MODULE: external "assert" var external_assert_ = __nccwpck_require__(2613); +// EXTERNAL MODULE: ./node_modules/@actions/exec/lib/exec.js + 2 modules +var lib_exec = __nccwpck_require__(5260); ;// CONCATENATED MODULE: ./node_modules/@actions/tool-cache/lib/retry-helper.js var retry_helper_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } @@ -35137,364 +35340,226 @@ function _unique(values) { return Array.from(new Set(values)); } //# sourceMappingURL=tool-cache.js.map -;// CONCATENATED MODULE: ./src/constants.ts -const MACOS_JAVA_CONTENT_POSTFIX = 'Contents/Home'; -const INPUT_JAVA_VERSION = 'java-version'; -const INPUT_JAVA_VERSION_FILE = 'java-version-file'; -const INPUT_ARCHITECTURE = 'architecture'; -const INPUT_JAVA_PACKAGE = 'java-package'; -const INPUT_DISTRIBUTION = 'distribution'; -const INPUT_JDK_FILE = 'jdk-file'; -const INPUT_JDK_FILE_DEPRECATED = 'jdkFile'; -const INPUT_CHECK_LATEST = 'check-latest'; -const INPUT_FORCE_DOWNLOAD = 'force-download'; -const INPUT_SET_DEFAULT = 'set-default'; -const INPUT_PROBLEM_MATCHER = 'problem-matcher'; -const INPUT_VERIFY_SIGNATURE = 'verify-signature'; -const INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = 'verify-signature-public-key'; -const INPUT_SERVER_ID = 'server-id'; -const INPUT_SERVER_USERNAME_ENV_VAR = 'server-username-env-var'; -const INPUT_SERVER_PASSWORD_ENV_VAR = 'server-password-env-var'; -const INPUT_SERVER_USERNAME_DEPRECATED = 'server-username'; -const INPUT_SERVER_PASSWORD_DEPRECATED = 'server-password'; -const INPUT_SETTINGS_PATH = 'settings-path'; -const INPUT_OVERWRITE_SETTINGS = 'overwrite-settings'; -const INPUT_GPG_PRIVATE_KEY = 'gpg-private-key'; -const INPUT_GPG_PASSPHRASE_ENV_VAR = 'gpg-passphrase-env-var'; -const INPUT_GPG_PASSPHRASE_DEPRECATED = 'gpg-passphrase'; -const INPUT_DEFAULT_SERVER_USERNAME = 'GITHUB_ACTOR'; -const INPUT_DEFAULT_SERVER_PASSWORD = 'GITHUB_TOKEN'; -const INPUT_DEFAULT_GPG_PRIVATE_KEY = (/* unused pure expression or super */ null && (undefined)); -const INPUT_DEFAULT_GPG_PASSPHRASE = 'GPG_PASSPHRASE'; -// The default name of the environment variable the maven-gpg-plugin reads the -// passphrase from (property `gpg.passphraseEnvName`). When the configured -// passphrase env var name matches this, no extra configuration is required. -const MAVEN_GPG_PASSPHRASE_DEFAULT_ENV = 'MAVEN_GPG_PASSPHRASE'; -// Id of the settings.xml profile used to set `gpg.passphraseEnvName`. -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'; -const M2_DIR = '.m2'; -const MVN_SETTINGS_FILE = 'settings.xml'; -const MVN_TOOLCHAINS_FILE = 'toolchains.xml'; -const INPUT_MVN_TOOLCHAIN_ID = 'mvn-toolchain-id'; -const INPUT_MVN_TOOLCHAIN_VENDOR = 'mvn-toolchain-vendor'; -const INPUT_SHOW_DOWNLOAD_PROGRESS = 'show-download-progress'; -const MAVEN_ARGS_ENV = 'MAVEN_ARGS'; -const MAVEN_NO_TRANSFER_PROGRESS_FLAG = '-ntp'; -const MAVEN_NO_TRANSFER_PROGRESS_LONG_FLAG = '--no-transfer-progress'; -const constants_DISTRIBUTIONS_ONLY_MAJOR_VERSION = (/* unused pure expression or super */ null && (['corretto'])); -;// CONCATENATED MODULE: ./src/util.ts +/***/ }) +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __nccwpck_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ var threw = true; +/******/ try { +/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __nccwpck_require__); +/******/ threw = false; +/******/ } finally { +/******/ if(threw) delete __webpack_module_cache__[moduleId]; +/******/ } +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __nccwpck_require__.m = __webpack_modules__; +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/compat get default export */ +/******/ (() => { +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __nccwpck_require__.n = (module) => { +/******/ var getter = module && module.__esModule ? +/******/ () => (module['default']) : +/******/ () => (module); +/******/ __nccwpck_require__.d(getter, { a: getter }); +/******/ return getter; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/create fake namespace object */ +/******/ (() => { +/******/ var getProto = Object.getPrototypeOf ? (obj) => (Object.getPrototypeOf(obj)) : (obj) => (obj.__proto__); +/******/ var leafPrototypes; +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 16: return value when it's Promise-like +/******/ // mode & 8|1: behave like require +/******/ __nccwpck_require__.t = function(value, mode) { +/******/ if(mode & 1) value = this(value); +/******/ if(mode & 8) return value; +/******/ if(typeof value === 'object' && value) { +/******/ if((mode & 4) && value.__esModule) return value; +/******/ if((mode & 16) && typeof value.then === 'function') return value; +/******/ } +/******/ var ns = Object.create(null); +/******/ __nccwpck_require__.r(ns); +/******/ var def = {}; +/******/ leafPrototypes = leafPrototypes || [null, getProto({}), getProto([]), getProto(getProto)]; +/******/ for(var current = mode & 2 && value; typeof current == 'object' && !~leafPrototypes.indexOf(current); current = getProto(current)) { +/******/ Object.getOwnPropertyNames(current).forEach((key) => (def[key] = () => (value[key]))); +/******/ } +/******/ def['default'] = () => (value); +/******/ __nccwpck_require__.d(ns, def); +/******/ return ns; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __nccwpck_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__nccwpck_require__.o(definition, key) && !__nccwpck_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/ensure chunk */ +/******/ (() => { +/******/ __nccwpck_require__.f = {}; +/******/ // This file contains only the entry chunk. +/******/ // The chunk loading function for additional chunks +/******/ __nccwpck_require__.e = (chunkId) => { +/******/ return Promise.all(Object.keys(__nccwpck_require__.f).reduce((promises, key) => { +/******/ __nccwpck_require__.f[key](chunkId, promises); +/******/ return promises; +/******/ }, [])); +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/get javascript chunk filename */ +/******/ (() => { +/******/ // This function allow to reference async chunks +/******/ __nccwpck_require__.u = (chunkId) => { +/******/ // return url for filenames based on template +/******/ return "" + chunkId + ".index.js"; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __nccwpck_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __nccwpck_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/compat */ +/******/ +/******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = new URL('.', import.meta.url).pathname.slice(import.meta.url.match(/^file:\/\/\/\w:/) ? 1 : 0, -1) + "/"; +/******/ +/******/ /* webpack/runtime/import chunk loading */ +/******/ (() => { +/******/ // no baseURI +/******/ +/******/ // object to store loaded and loading chunks +/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched +/******/ // [resolve, Promise] = chunk loading, 0 = chunk loaded +/******/ var installedChunks = { +/******/ 792: 0 +/******/ }; +/******/ +/******/ var installChunk = (data) => { +/******/ var {ids, modules, runtime} = data; +/******/ // add "modules" to the modules object, +/******/ // then flag all "ids" as loaded and fire callback +/******/ var moduleId, chunkId, i = 0; +/******/ for(moduleId in modules) { +/******/ if(__nccwpck_require__.o(modules, moduleId)) { +/******/ __nccwpck_require__.m[moduleId] = modules[moduleId]; +/******/ } +/******/ } +/******/ if(runtime) runtime(__nccwpck_require__); +/******/ for(;i < ids.length; i++) { +/******/ chunkId = ids[i]; +/******/ if(__nccwpck_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) { +/******/ installedChunks[chunkId][0](); +/******/ } +/******/ installedChunks[ids[i]] = 0; +/******/ } +/******/ +/******/ } +/******/ +/******/ __nccwpck_require__.f.j = (chunkId, promises) => { +/******/ // import() chunk loading for javascript +/******/ var installedChunkData = __nccwpck_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined; +/******/ if(installedChunkData !== 0) { // 0 means "already installed". +/******/ +/******/ // a Promise means "currently loading". +/******/ if(installedChunkData) { +/******/ promises.push(installedChunkData[1]); +/******/ } else { +/******/ if(true) { // all chunks have JS +/******/ // setup Promise in chunk cache +/******/ var promise = import("./" + __nccwpck_require__.u(chunkId)).then(installChunk, (e) => { +/******/ if(installedChunks[chunkId] !== 0) installedChunks[chunkId] = undefined; +/******/ throw e; +/******/ }); +/******/ var promise = Promise.race([promise, new Promise((resolve) => (installedChunkData = installedChunks[chunkId] = [resolve]))]) +/******/ promises.push(installedChunkData[1] = promise); +/******/ } +/******/ } +/******/ } +/******/ }; +/******/ +/******/ // no prefetching +/******/ +/******/ // no preloaded +/******/ +/******/ // no external install chunk +/******/ +/******/ // no on chunks loaded +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// EXPORTS +__nccwpck_require__.d(__webpack_exports__, { + e: () => (/* binding */ run) +}); - - - - -function getTempDir() { - const tempDirectory = process.env['RUNNER_TEMP'] || external_os_default().tmpdir(); - return tempDirectory; -} -function getBooleanInput(inputName, defaultValue = false) { - const inputValue = lib_core/* getInput */.V4(inputName); - const normalizedValue = inputValue.trim().toLowerCase(); - if (!normalizedValue) { - return defaultValue; - } - if (normalizedValue === 'true') { - return true; - } - if (normalizedValue === 'false') { - return false; - } - throw new Error(`Invalid value '${inputValue}' for boolean input '${inputName}'. Expected 'true' or 'false'.`); -} -function getVersionFromToolcachePath(toolPath) { - if (toolPath) { - return path.basename(path.dirname(toolPath)); - } - return toolPath; -} -async function extractJdkFile(toolPath, extension) { - if (!extension) { - extension = toolPath.endsWith('.tar.gz') - ? 'tar.gz' - : path.extname(toolPath); - if (extension.startsWith('.')) { - extension = extension.substring(1); - } - } - switch (extension) { - case 'tar.gz': - case 'tar': - return await tc.extractTar(toolPath); - case 'zip': - return await tc.extractZip(toolPath); - default: - return await tc.extract7z(toolPath); - } -} -function getDownloadArchiveExtension() { - return process.platform === 'win32' ? 'zip' : 'tar.gz'; -} -function isVersionSatisfies(range, version) { - // Some distributions (e.g. JetBrains Runtime) publish 4-segment versions - // like '17.0.8.1+1080.1' that semver rejects. If the candidate version - // isn't valid semver, it can't match — bail out rather than letting - // compareBuild / satisfies throw. - if (!semver.valid(version)) { - return false; - } - if (semver.valid(range)) { - // if full version with build digit is provided as a range (such as '1.2.3+4') - // we should check for exact equal via compareBuild - // since semver.satisfies doesn't handle 4th digit - const semRange = semver.parse(range); - if (semRange && semRange.build?.length > 0) { - return semver.compareBuild(range, version) === 0; - } - } - return semver.satisfies(version, range); -} -function getToolcachePath(toolName, version, architecture) { - const toolcacheRoot = process.env['RUNNER_TOOL_CACHE'] ?? ''; - const fullPath = path.join(toolcacheRoot, toolName, version, architecture); - if (fs.existsSync(fullPath)) { - return fullPath; - } - return null; -} -function isJobStatusSuccess() { - const jobStatus = lib_core/* getInput */.V4(INPUT_JOB_STATUS); - return jobStatus === 'success'; -} -function isGhes() { - const ghUrl = new URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com'); - const hostname = ghUrl.hostname.trimEnd().toUpperCase(); - const isGitHubHost = hostname === 'GITHUB.COM'; - const isGitHubEnterpriseCloudHost = hostname.endsWith('.GHE.COM'); - const isLocalHost = hostname.endsWith('.LOCALHOST'); - return !isGitHubHost && !isGitHubEnterpriseCloudHost && !isLocalHost; -} -function getVersionFromFileContent(content, distributionName, versionFile) { - let javaVersionRegExp; - let extractedDistribution; - function getFileName(versionFile) { - return path.basename(versionFile); - } - const versionFileName = getFileName(versionFile); - if (versionFileName == '.tool-versions') { - // Capture an optional asdf-java vendor prefix (e.g. `temurin-`, `corretto-`) - // in the `distribution` group so it can be mapped to a setup-java distribution. - javaVersionRegExp = - /^java\s+(?:(?\S*)-)?(?\d+(?:\.\d+)*([+_.-](?:openj9[-._]?\d[\w.-]*|java\d+|jre[-_\w]*|OpenJDK\d+[\w_.-]*|[a-z0-9]+))*)/im; - } - else if (versionFileName == '.sdkmanrc') { - // Match both version and optional distribution identifier - javaVersionRegExp = - /^java\s*=\s*(?[^-\s]+)(?:-(?[a-z0-9]+))?/m; - } - else { - javaVersionRegExp = /(?(?<=(^|\s|-))(\d+\S*))(\s|$)/; - } - const match = content.match(javaVersionRegExp); - const capturedVersion = match?.groups?.version - ? match.groups.version - : ''; - // Extract distribution from .sdkmanrc file - if (versionFileName == '.sdkmanrc' && match?.groups?.distribution) { - const sdkmanDist = match.groups.distribution; - extractedDistribution = mapSdkmanDistribution(sdkmanDist); - core.debug(`Parsed distribution '${extractedDistribution}' from SDKMAN identifier '${sdkmanDist}'`); - } - // Extract distribution from asdf .tool-versions file - if (versionFileName == '.tool-versions' && match?.groups?.distribution) { - const asdfDist = match.groups.distribution; - extractedDistribution = mapAsdfDistribution(asdfDist); - if (extractedDistribution) { - core.debug(`Parsed distribution '${extractedDistribution}' from asdf identifier '${asdfDist}'`); - } - } - core.debug(`Parsed version '${capturedVersion}' from file '${versionFileName}'`); - if (!capturedVersion) { - return null; - } - const tentativeVersion = avoidOldNotation(capturedVersion); - const rawVersion = tentativeVersion.split('-')[0]; - let version = semver.validRange(rawVersion) - ? tentativeVersion - : semver.coerce(tentativeVersion); - core.debug(`Range version from file is '${version}'`); - if (!version) { - return null; - } - // Apply DISTRIBUTIONS_ONLY_MAJOR_VERSION logic whenever the effective distribution - // (either explicitly provided or extracted from the version file) is in the list. - if (DISTRIBUTIONS_ONLY_MAJOR_VERSION.includes(extractedDistribution || distributionName)) { - const coerceVersion = semver.coerce(version) ?? version; - version = semver.major(coerceVersion).toString(); - } - return { - version: version.toString(), - distribution: extractedDistribution - }; -} -// Map SDKMAN distribution identifiers to setup-java distribution names -function mapSdkmanDistribution(sdkmanDist) { - const distributionMap = { - tem: 'temurin', - sem: 'semeru', - albba: 'dragonwell', - zulu: 'zulu', - amzn: 'corretto', - graal: 'graalvm', - graalce: 'graalvm', - librca: 'liberica', - ms: 'microsoft', - oracle: 'oracle', - sapmchn: 'sapmachine', - jbr: 'jetbrains', - dragonwell: 'dragonwell', - kona: 'kona' - }; - const mapped = distributionMap[sdkmanDist.toLowerCase()]; - if (!mapped) { - core.warning(`Unknown SDKMAN distribution identifier '${sdkmanDist}'. Please specify the distribution explicitly.`); - } - return mapped; -} -// Map asdf-java (.tool-versions) vendor identifiers to setup-java distribution names. -// asdf-java encodes the vendor as a prefix on the version string, e.g. -// `java temurin-17.0.3+7` or `java semeru-openj9-11.0.25+9`. Packaging variants -// (`-jre`, `-musl`, `-openj9`, `-crac`, `-javafx`, ...) are collapsed onto the -// base vendor since setup-java does not distinguish them here. -function mapAsdfDistribution(asdfDist) { - const normalized = asdfDist.toLowerCase(); - // Multi-segment vendors that map to a distinct setup-java distribution. - if (normalized.startsWith('graalvm-community')) { - return 'graalvm-community'; - } - if (normalized.startsWith('oracle-graalvm')) { - return 'graalvm'; - } - const baseVendor = normalized.split('-')[0]; - const distributionMap = { - temurin: 'temurin', - adoptopenjdk: 'temurin', - zulu: 'zulu', - corretto: 'corretto', - liberica: 'liberica', - microsoft: 'microsoft', - semeru: 'semeru', - ibm: 'semeru', - dragonwell: 'dragonwell', - graalvm: 'graalvm', - oracle: 'oracle', - sapmachine: 'sapmachine', - kona: 'kona', - jetbrains: 'jetbrains' - }; - const mapped = distributionMap[baseVendor]; - if (!mapped) { - core.warning(`Unknown asdf distribution identifier '${asdfDist}'. Please specify the distribution explicitly.`); - } - return mapped; -} -// By convention, action expects version 8 in the format `8.*` instead of `1.8` -function avoidOldNotation(content) { - return content.startsWith('1.') ? content.substring(2) : content; -} -function convertVersionToSemver(version) { - // Some distributions may use semver-like notation (12.10.2.1, 12.10.2.1.1) - const versionArray = Array.isArray(version) ? version : version.split('.'); - const mainVersion = versionArray.slice(0, 3).join('.'); - if (versionArray.length > 3) { - return `${mainVersion}+${versionArray.slice(3).join('.')}`; - } - return mainVersion; -} -function getGitHubHttpHeaders() { - const resolvedToken = core.getInput('token') || process.env.GITHUB_TOKEN; - const auth = !resolvedToken ? undefined : `token ${resolvedToken}`; - const headers = { - accept: 'application/vnd.github.VERSION.raw' - }; - if (auth) { - headers.authorization = auth; - } - return headers; -} -const MAX_PAGINATION_PAGES = 1000; -function getNextPageUrlFromLinkHeader(headers) { - if (!headers) { - return null; - } - const linkHeader = headers.link ?? headers.Link; - if (!linkHeader) { - return null; - } - const normalizedLinkHeader = Array.isArray(linkHeader) - ? linkHeader.join(',') - : linkHeader; - // Split into individual link-values and find the one with rel="next" - // RFC 8288 allows rel to appear anywhere among the parameters - const linkValues = normalizedLinkHeader.split(/,(?=\s*<)/); - for (const linkValue of linkValues) { - const urlMatch = linkValue.match(/<([^>]+)>/); - if (!urlMatch) - continue; - const params = linkValue.slice(urlMatch[0].length); - // Use word boundary to match "next" as a standalone relation type - // RFC 8288 allows space-separated relation types like rel="next prev" - if (/;\s*rel="?[^"]*\bnext\b/i.test(params)) { - return urlMatch[1]; - } - } - return null; -} -function validatePaginationUrl(url, allowedOrigin) { - try { - const parsed = new URL(url); - const allowed = new URL(allowedOrigin); - return parsed.origin === allowed.origin; - } - catch { - return false; - } -} -// Rename archive to add extension because after downloading -// archive does not contain extension type and it leads to some issues -// on Windows runners without PowerShell Core. -// -// For default PowerShell Windows it should contain extension type to unpack it. -function renameWinArchive(javaArchivePath) { - const javaArchivePathRenamed = `${javaArchivePath}.zip`; - fs.renameSync(javaArchivePath, javaArchivePathRenamed); - return javaArchivePathRenamed; -} -// Resolve the newest available stable/GA feature (major) release. -// -// Some distributions (e.g. Oracle, GraalVM) construct their download URLs from a -// concrete major version and don't expose an endpoint to list every available -// release, so a bare `latest` alias can't be resolved from their own metadata. -// The Adoptium (Temurin) API is used as a proxy for "what is the newest GA major -// version out there", which those distributions typically publish at the same time. -async function getLatestMajorVersion(http) { - const availableReleasesUrl = 'https://api.adoptium.net/v3/info/available_releases'; - const response = await http.getJson(availableReleasesUrl); - const mostRecent = response.result?.most_recent_feature_release; - if (!mostRecent || Number.isNaN(Number(mostRecent))) { - throw new Error(`Could not determine the latest available Java major version from ${availableReleasesUrl}`); - } - return Number(mostRecent); -} - +// EXTERNAL MODULE: ./node_modules/@actions/core/lib/core.js + 7 modules +var cleanup_java_core = __nccwpck_require__(3838); +// EXTERNAL MODULE: external "fs" +var external_fs_ = __nccwpck_require__(9896); +// EXTERNAL MODULE: external "path" +var external_path_ = __nccwpck_require__(6928); +// EXTERNAL MODULE: ./node_modules/@actions/io/lib/io.js +var lib_io = __nccwpck_require__(8701); +// EXTERNAL MODULE: ./node_modules/@actions/exec/lib/exec.js + 2 modules +var lib_exec = __nccwpck_require__(5260); +// EXTERNAL MODULE: ./node_modules/@actions/tool-cache/lib/tool-cache.js + 2 modules +var tool_cache = __nccwpck_require__(9805); +// EXTERNAL MODULE: ./src/util.ts +var src_util = __nccwpck_require__(4527); ;// CONCATENATED MODULE: ./src/gpg.ts @@ -35502,7 +35567,7 @@ async function getLatestMajorVersion(http) { -const PRIVATE_KEY_FILE = external_path_.join(getTempDir(), 'private-key.asc'); +const PRIVATE_KEY_FILE = external_path_.join(src_util/* getTempDir */.G4(), 'private-key.asc'); const PRIVATE_KEY_FINGERPRINT_REGEX = /\w{40}/; // Convert a Windows path (D:\a\_temp\...) to a POSIX path (/d/a/_temp/...). // The Git-bundled GPG on Windows (MSYS2-based) uses POSIX path conventions @@ -35586,6 +35651,8 @@ async function verifyPackageSignature(archivePath, signatureUrl, publicKeyConten } } +// EXTERNAL MODULE: ./src/constants.ts +var constants = __nccwpck_require__(7242); // EXTERNAL MODULE: external "url" var external_url_ = __nccwpck_require__(7016); ;// CONCATENATED MODULE: ./src/cleanup-java.ts @@ -35595,14 +35662,14 @@ var external_url_ = __nccwpck_require__(7016); async function removePrivateKeyFromKeychain() { - if (lib_core/* getInput */.V4(INPUT_GPG_PRIVATE_KEY, { required: false })) { - lib_core/* info */.pq('Removing private key from keychain'); + if (cleanup_java_core/* getInput */.V4(constants/* INPUT_GPG_PRIVATE_KEY */.wz, { required: false })) { + cleanup_java_core/* info */.pq('Removing private key from keychain'); try { - const keyFingerprint = lib_core/* getState */.Gu(STATE_GPG_PRIVATE_KEY_FINGERPRINT); + const keyFingerprint = cleanup_java_core/* getState */.Gu(constants/* STATE_GPG_PRIVATE_KEY_FINGERPRINT */.wm); await deleteKey(keyFingerprint); } catch (error) { - lib_core/* setFailed */.C1(`Failed to remove private key due to: ${error.message}`); + cleanup_java_core/* setFailed */.C1(`Failed to remove private key due to: ${error.message}`); } } } @@ -35610,18 +35677,27 @@ async function removePrivateKeyFromKeychain() { * Check given input and run a save process for the specified package manager * @returns Promise that will be resolved when the save process finishes */ -async function saveCache() { - const jobStatus = isJobStatusSuccess(); - const cache = lib_core/* getInput */.V4(INPUT_CACHE); - if (!jobStatus || !cache) { +async function saveCaches() { + const jobStatus = (0,src_util/* isJobStatusSuccess */.TX)(); + const cache = cleanup_java_core/* getInput */.V4(constants/* INPUT_CACHE */.gk); + const cacheJdk = (0,src_util/* isJdkCacheEnabled */.lN)(cache); + if (!jobStatus || (!cache && !cacheJdk)) { return; } - if (getBooleanInput(INPUT_CACHE_READ_ONLY, false)) { - lib_core/* info */.pq('Cache saving is skipped because cache-read-only is enabled.'); + if ((0,src_util/* getBooleanInput */.Vt)(constants/* INPUT_CACHE_READ_ONLY */.Ch, false)) { + cleanup_java_core/* info */.pq('Cache saving is skipped because cache-read-only is enabled.'); return; } - const { save } = await Promise.all(/* import() */[__nccwpck_require__.e(767), __nccwpck_require__.e(377)]).then(__nccwpck_require__.bind(__nccwpck_require__, 7377)); - await save(cache); + const saves = []; + if (cache) { + const { save } = await Promise.all(/* import() */[__nccwpck_require__.e(767), __nccwpck_require__.e(377)]).then(__nccwpck_require__.bind(__nccwpck_require__, 7377)); + saves.push(save(cache)); + } + if (cacheJdk) { + const { saveJdkCaches } = await Promise.all(/* import() */[__nccwpck_require__.e(767), __nccwpck_require__.e(314)]).then(__nccwpck_require__.bind(__nccwpck_require__, 2314)); + saves.push(saveJdkCaches()); + } + await Promise.all(saves); } /** * The save process is best-effort, and it should not make the workflow fail @@ -35633,7 +35709,7 @@ async function ignoreError(promise) { return new Promise(resolve => { promise .catch(error => { - lib_core/* warning */.$e(error); + cleanup_java_core/* warning */.$e(error); resolve(void 0); }) .then(resolve); @@ -35641,14 +35717,14 @@ async function ignoreError(promise) { } async function run() { await removePrivateKeyFromKeychain(); - await ignoreError(saveCache()); + await ignoreError(saveCaches()); } if (process.argv[1] === (0,external_url_.fileURLToPath)(import.meta.url)) { run(); } else { // https://nodejs.org/api/modules.html#modules_accessing_the_main_module - lib_core/* info */.pq('the script is loaded as a module, so skipping the execution'); + cleanup_java_core/* info */.pq('the script is loaded as a module, so skipping the execution'); } var __webpack_exports__run = __webpack_exports__.e; diff --git a/dist/setup/19.index.js b/dist/setup/19.index.js index 5b3a5918..77843a5c 100644 --- a/dist/setup/19.index.js +++ b/dist/setup/19.index.js @@ -16,7 +16,11 @@ export const modules = { /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_3__); /* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(6242); /* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(4527); -/* harmony import */ var _constants_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(7242); +/* harmony import */ var _constants_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(7242); +/* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(6982); +/* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(crypto__WEBPACK_IMPORTED_MODULE_6__); + + @@ -34,6 +38,9 @@ class LocalDistribution extends _base_installer_js__WEBPACK_IMPORTED_MODULE_4__/ if (this.latest) { throw new Error("The 'latest' version alias is not supported for the 'jdkfile' distribution. Please specify a concrete version."); } + if (this.verifySignature) { + throw new Error(`Input 'verify-signature' is not supported for distribution '${this.distribution}'.`); + } let foundJava = this.forceDownload ? null : this.findInToolcache(); if (foundJava) { _actions_core__WEBPACK_IMPORTED_MODULE_1__/* .info */ .pq(`Resolved Java ${foundJava.version} from tool-cache`); @@ -48,19 +55,54 @@ class LocalDistribution extends _base_installer_js__WEBPACK_IMPORTED_MODULE_4__/ if (!stats.isFile()) { throw new Error(`JDK file was not found in path '${jdkFilePath}'`); } - _actions_core__WEBPACK_IMPORTED_MODULE_1__/* .info */ .pq(`Extracting Java from '${jdkFilePath}'`); - const extractedJavaPath = await (0,_util_js__WEBPACK_IMPORTED_MODULE_5__/* .extractJdkFile */ .PE)(jdkFilePath); - const archiveName = fs__WEBPACK_IMPORTED_MODULE_2___default().readdirSync(extractedJavaPath)[0]; - const archivePath = path__WEBPACK_IMPORTED_MODULE_3___default().join(extractedJavaPath, archiveName); - const javaVersion = this.version; - const javaPath = await _actions_tool_cache__WEBPACK_IMPORTED_MODULE_0__/* .cacheDir */ .e8(archivePath, this.toolcacheFolderName, this.getToolcacheVersionName(javaVersion), this.architecture); - foundJava = { - version: javaVersion, - path: javaPath - }; + let jdkCache; + if (this.cacheJdk) { + const [{ getJdkVerificationIdentity }, source] = await Promise.all([ + Promise.all(/* import() */[__webpack_require__.e(824), __webpack_require__.e(971), __webpack_require__.e(779)]).then(__webpack_require__.bind(__webpack_require__, 5779)), + hashFile(jdkFilePath) + ]); + jdkCache = { + distribution: this.distribution, + packageType: this.packageType, + architecture: this.architecture, + version: this.version, + source, + verification: getJdkVerificationIdentity(false), + path: this.getJdkCachePath(this.version) + }; + } + if (!this.forceDownload && jdkCache) { + const { restoreJdk } = await Promise.all(/* import() */[__webpack_require__.e(824), __webpack_require__.e(971), __webpack_require__.e(779)]).then(__webpack_require__.bind(__webpack_require__, 5779)); + const restored = await restoreJdk(jdkCache); + const restoredPath = restored + ? this.getRestoredJdkPath(this.version) + : undefined; + if (restoredPath) { + foundJava = { + version: this.version, + path: restoredPath + }; + } + } + if (!foundJava) { + _actions_core__WEBPACK_IMPORTED_MODULE_1__/* .info */ .pq(`Extracting Java from '${jdkFilePath}'`); + const extractedJavaPath = await (0,_util_js__WEBPACK_IMPORTED_MODULE_5__/* .extractJdkFile */ .PE)(jdkFilePath); + const archiveName = fs__WEBPACK_IMPORTED_MODULE_2___default().readdirSync(extractedJavaPath)[0]; + const archivePath = path__WEBPACK_IMPORTED_MODULE_3___default().join(extractedJavaPath, archiveName); + const javaVersion = this.version; + const javaPath = await _actions_tool_cache__WEBPACK_IMPORTED_MODULE_0__/* .cacheDir */ .e8(archivePath, this.toolcacheFolderName, this.getToolcacheVersionName(javaVersion), this.architecture); + foundJava = { + version: javaVersion, + path: javaPath + }; + if (jdkCache) { + const { registerJdk } = await Promise.all(/* import() */[__webpack_require__.e(824), __webpack_require__.e(971), __webpack_require__.e(779)]).then(__webpack_require__.bind(__webpack_require__, 5779)); + registerJdk(jdkCache); + } + } } // JDK folder may contain postfix "Contents/Home" on macOS - const macOSPostfixPath = path__WEBPACK_IMPORTED_MODULE_3___default().join(foundJava.path, _constants_js__WEBPACK_IMPORTED_MODULE_6__/* .MACOS_JAVA_CONTENT_POSTFIX */ .PG); + const macOSPostfixPath = path__WEBPACK_IMPORTED_MODULE_3___default().join(foundJava.path, _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MACOS_JAVA_CONTENT_POSTFIX */ .PG); if (process.platform === 'darwin' && fs__WEBPACK_IMPORTED_MODULE_2___default().existsSync(macOSPostfixPath)) { foundJava.path = macOSPostfixPath; } @@ -83,6 +125,13 @@ class LocalDistribution extends _base_installer_js__WEBPACK_IMPORTED_MODULE_4__/ throw new Error('This method should not be implemented in local file provider'); } } +async function hashFile(file) { + const hash = (0,crypto__WEBPACK_IMPORTED_MODULE_6__.createHash)('sha256'); + for await (const chunk of (0,fs__WEBPACK_IMPORTED_MODULE_2__.createReadStream)(file)) { + hash.update(chunk); + } + return hash.digest('hex'); +} /***/ }) diff --git a/dist/setup/242.index.js b/dist/setup/242.index.js index 2564989e..75d8153a 100644 --- a/dist/setup/242.index.js +++ b/dist/setup/242.index.js @@ -217,6 +217,7 @@ class JavaBase { latest; checkLatest; forceDownload; + cacheJdk; setDefault; verifySignature; verifySignaturePublicKey; @@ -232,6 +233,7 @@ class JavaBase { this.packageType = installerOptions.packageType; this.checkLatest = installerOptions.checkLatest; this.forceDownload = installerOptions.forceDownload ?? false; + this.cacheJdk = installerOptions.cacheJdk ?? false; this.setDefault = installerOptions.setDefault !== undefined ? installerOptions.setDefault @@ -326,9 +328,44 @@ class JavaBase { core/* info */.pq(`Resolved Java ${foundJava.version} from tool-cache`); } else { - core/* info */.pq('Trying to download...'); - foundJava = await this.downloadTool(javaRelease); - core/* info */.pq(`Java ${foundJava.version} was downloaded`); + let jdkCache; + if (this.cacheJdk) { + const { getJdkVerificationIdentity } = await Promise.all(/* import() */[__webpack_require__.e(824), __webpack_require__.e(971), __webpack_require__.e(779)]).then(__webpack_require__.bind(__webpack_require__, 5779)); + jdkCache = { + distribution: this.distribution, + packageType: this.packageType, + architecture: this.architecture, + version: javaRelease.version, + source: this.getJdkReleaseIdentity(javaRelease), + verification: getJdkVerificationIdentity(this.verifySignature, this.verifySignaturePublicKey), + path: this.getJdkCachePath(javaRelease.version) + }; + } + if (!this.forceDownload && jdkCache) { + const { restoreJdk } = await Promise.all(/* import() */[__webpack_require__.e(824), __webpack_require__.e(971), __webpack_require__.e(779)]).then(__webpack_require__.bind(__webpack_require__, 5779)); + const restored = await restoreJdk(jdkCache); + if (restored) { + const restoredPath = this.getRestoredJdkPath(javaRelease.version); + if (restoredPath) { + foundJava = { + version: javaRelease.version, + path: restoredPath + }; + } + } + } + if (!foundJava || foundJava.version !== javaRelease.version) { + core/* info */.pq('Trying to download...'); + foundJava = await this.downloadTool(javaRelease); + core/* info */.pq(`Java ${foundJava.version} was downloaded`); + if (jdkCache) { + // Register after the installation exists so its identity is + // captured; the post-job save refuses to upload a path whose + // installation was replaced afterwards. + const { registerJdk } = await Promise.all(/* import() */[__webpack_require__.e(824), __webpack_require__.e(971), __webpack_require__.e(779)]).then(__webpack_require__.bind(__webpack_require__, 5779)); + registerJdk(jdkCache); + } + } } } catch (error) { @@ -435,6 +472,36 @@ class JavaBase { // related issue: https://github.com/actions/virtual-environments/issues/3014 return version.replace('+', '-'); } + getJdkCachePath(version) { + const toolCache = process.env['RUNNER_TOOL_CACHE']; + if (!toolCache) { + return ''; + } + return external_path_default().join(toolCache, this.toolcacheFolderName, this.getToolcacheVersionName(version)); + } + getRestoredJdkPath(version) { + const basePath = this.getJdkCachePath(version); + if (!basePath) { + return null; + } + const architecturePath = external_path_default().join(basePath, this.architecture); + return external_fs_.existsSync(architecturePath) && + external_fs_.existsSync(`${architecturePath}.complete`) + ? architecturePath + : null; + } + getJdkReleaseIdentity(javaRelease) { + if (javaRelease.checksum) { + return `${javaRelease.checksum.algorithm}:${javaRelease.checksum.value}`; + } + try { + const url = new URL(javaRelease.url); + return `${url.origin}${url.pathname}`; + } + catch { + return javaRelease.url; + } + } findInToolcache() { // we can't use tc.find directly because firstly, we need to filter versions by stability flag // if *-ea is provided, take only ea versions from toolcache, otherwise - only stable versions diff --git a/dist/setup/779.index.js b/dist/setup/779.index.js new file mode 100644 index 00000000..ff832e63 --- /dev/null +++ b/dist/setup/779.index.js @@ -0,0 +1,229 @@ +export const id = 779; +export const ids = [779,394]; +export const modules = { + +/***/ 1394: +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ isCacheFeatureAvailable: () => (/* binding */ isCacheFeatureAvailable) +/* harmony export */ }); +/* harmony import */ var _actions_cache__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6971); +/* harmony import */ var _actions_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3838); +/* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4527); + + + +function isCacheFeatureAvailable() { + if (_actions_cache__WEBPACK_IMPORTED_MODULE_0__/* .isFeatureAvailable */ .w3()) { + return true; + } + if ((0,_util_js__WEBPACK_IMPORTED_MODULE_2__/* .isGhes */ .aT)()) { + _actions_core__WEBPACK_IMPORTED_MODULE_1__/* .warning */ .$e('Caching is only supported on GHES version >= 3.5. If you are on a version >= 3.5, please check with your GHES admin if the Actions cache service is enabled or not.'); + return false; + } + _actions_core__WEBPACK_IMPORTED_MODULE_1__/* .warning */ .$e('The runner was not able to contact the cache service. Caching will be skipped'); + return false; +} + + +/***/ }), + +/***/ 5779: +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ buildJdkCacheKey: () => (/* binding */ buildJdkCacheKey), +/* harmony export */ getJdkVerificationIdentity: () => (/* binding */ getJdkVerificationIdentity), +/* harmony export */ registerJdk: () => (/* binding */ registerJdk), +/* harmony export */ restoreJdk: () => (/* binding */ restoreJdk), +/* harmony export */ saveJdkCaches: () => (/* binding */ saveJdkCaches) +/* harmony export */ }); +/* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6982); +/* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(crypto__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9896); +/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6928); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var _actions_cache__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6971); +/* harmony import */ var _actions_core__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(3838); +/* harmony import */ var _cache_feature_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(1394); + + + + + + +const STATE_JDK_CACHES = 'jdk-caches'; +const JDK_CACHE_KEY_VERSION = 1; +const restoredCaches = []; +async function restoreJdk(jdk) { + if (!jdk.path || !(0,_cache_feature_js__WEBPACK_IMPORTED_MODULE_5__.isCacheFeatureAvailable)()) { + return false; + } + const key = buildJdkCacheKey(jdk); + let matchedKey; + try { + matchedKey = await _actions_cache__WEBPACK_IMPORTED_MODULE_3__/* .restoreCache */ .P3([jdk.path], key); + } + catch (error) { + _actions_core__WEBPACK_IMPORTED_MODULE_4__/* .warning */ .$e(`Failed to restore JDK cache: ${error.message}`); + } + const architecturePath = path__WEBPACK_IMPORTED_MODULE_2___default().join(jdk.path, jdk.architecture); + if (matchedKey && + (!fs__WEBPACK_IMPORTED_MODULE_1___default().existsSync(architecturePath) || + !fs__WEBPACK_IMPORTED_MODULE_1___default().existsSync(`${architecturePath}.complete`))) { + _actions_core__WEBPACK_IMPORTED_MODULE_4__/* .warning */ .$e(`JDK cache key ${matchedKey} was restored without the expected tool-cache path; downloading the JDK instead.`); + matchedKey = undefined; + } + recordJdkCache({ + key, + path: jdk.path, + architecture: jdk.architecture, + matchedKey + }); + if (matchedKey) { + _actions_core__WEBPACK_IMPORTED_MODULE_4__/* .info */ .pq(`JDK cache restored from key: ${matchedKey}`); + return true; + } + _actions_core__WEBPACK_IMPORTED_MODULE_4__/* .info */ .pq(`JDK cache is not found for ${jdk.distribution} ${jdk.version}`); + return false; +} +function registerJdk(jdk) { + if (!jdk.path) { + return; + } + recordJdkCache({ + key: buildJdkCacheKey(jdk), + path: jdk.path, + architecture: jdk.architecture, + installation: getInstallationIdentity(jdk.path, jdk.architecture) + }); +} +/** + * Cheap fingerprint of the installation stored at a tool-cache path. The + * `.complete` marker is (re)created by `tc.cacheDir` every time an + * installation is written, so its inode and timestamps change whenever the + * installation is replaced. This avoids rehashing a multi-hundred-megabyte JDK + * directory while still detecting that the bytes behind a key were swapped. + */ +function getInstallationIdentity(jdkPath, architecture) { + const architecturePath = path__WEBPACK_IMPORTED_MODULE_2___default().join(jdkPath, architecture); + try { + const marker = fs__WEBPACK_IMPORTED_MODULE_1___default().statSync(`${architecturePath}.complete`); + const installation = fs__WEBPACK_IMPORTED_MODULE_1___default().statSync(architecturePath); + return [ + marker.ino, + marker.mtimeMs, + marker.ctimeMs, + marker.size, + installation.ino, + installation.mtimeMs, + installation.ctimeMs + ].join(':'); + } + catch { + return undefined; + } +} +function getJdkVerificationIdentity(verifySignature, publicKey) { + if (!verifySignature) { + return 'unverified'; + } + if (!publicKey) { + return 'verified:bundled'; + } + const normalizedKey = publicKey.replace(/\r\n?/g, '\n').trim(); + const fingerprint = (0,crypto__WEBPACK_IMPORTED_MODULE_0__.createHash)('sha256').update(normalizedKey).digest('hex'); + return `verified:custom:sha256:${fingerprint}`; +} +async function saveJdkCaches() { + const state = _actions_core__WEBPACK_IMPORTED_MODULE_4__/* .getState */ .Gu(STATE_JDK_CACHES); + if (!state) { + return; + } + const caches = parseJdkCacheState(state); + for (const jdk of caches) { + if (jdk.matchedKey === jdk.key) { + _actions_core__WEBPACK_IMPORTED_MODULE_4__/* .info */ .pq(`Cache hit occurred on the JDK primary key ${jdk.key}, not saving cache.`); + continue; + } + if (!fs__WEBPACK_IMPORTED_MODULE_1___default().existsSync(jdk.path)) { + _actions_core__WEBPACK_IMPORTED_MODULE_4__/* .debug */ .Yz(`JDK cache path does not exist, not saving: ${jdk.path}`); + continue; + } + if (!jdk.installation) { + _actions_core__WEBPACK_IMPORTED_MODULE_4__/* .debug */ .Yz(`No JDK installation was registered for the key ${jdk.key}, not saving cache.`); + continue; + } + if (getInstallationIdentity(jdk.path, jdk.architecture) !== jdk.installation) { + _actions_core__WEBPACK_IMPORTED_MODULE_4__/* .warning */ .$e(`The JDK installation in ${jdk.path} was replaced after it was registered for the key ${jdk.key}; not saving cache.`); + continue; + } + try { + const cacheId = await _actions_cache__WEBPACK_IMPORTED_MODULE_3__/* .saveCache */ .Io([jdk.path], jdk.key); + if (cacheId !== -1) { + _actions_core__WEBPACK_IMPORTED_MODULE_4__/* .info */ .pq(`JDK cache saved with the key: ${jdk.key}`); + } + } + catch (error) { + const err = error; + if (err.name === _actions_cache__WEBPACK_IMPORTED_MODULE_3__/* .ReserveCacheError */ .Zh.name) { + _actions_core__WEBPACK_IMPORTED_MODULE_4__/* .info */ .pq(err.message); + } + else { + // Saving is best-effort and per entry: one failure must not suppress + // the remaining JDK caches. + _actions_core__WEBPACK_IMPORTED_MODULE_4__/* .warning */ .$e(`Failed to save the JDK cache with the key ${jdk.key}: ${err.message}`); + } + } + } +} +function buildJdkCacheKey(jdk) { + const runnerOs = process.env['RUNNER_OS'] ?? process.platform; + const normalizedArchitecture = jdk.architecture.toLowerCase(); + const identity = JSON.stringify({ + keyVersion: JDK_CACHE_KEY_VERSION, + runnerOs, + distribution: jdk.distribution.toLowerCase(), + packageType: jdk.packageType.toLowerCase(), + architecture: normalizedArchitecture, + version: jdk.version, + source: jdk.source, + verification: jdk.verification + }); + const digest = (0,crypto__WEBPACK_IMPORTED_MODULE_0__.createHash)('sha256').update(identity).digest('hex'); + return `setup-java-jdk-v${JDK_CACHE_KEY_VERSION}-${runnerOs}-${normalizedArchitecture}-${digest}`; +} +function recordJdkCache(jdk) { + const existing = restoredCaches.findIndex(item => item.key === jdk.key && item.path === jdk.path); + if (existing === -1) { + restoredCaches.push(jdk); + } + else { + restoredCaches[existing] = { ...restoredCaches[existing], ...jdk }; + } + _actions_core__WEBPACK_IMPORTED_MODULE_4__/* .saveState */ .LZ(STATE_JDK_CACHES, JSON.stringify(restoredCaches)); +} +function parseJdkCacheState(state) { + const value = JSON.parse(state); + if (!Array.isArray(value) || + !value.every(item => typeof item === 'object' && + item !== null && + typeof item.key === 'string' && + typeof item.path === 'string' && + typeof item.architecture === 'string' && + (item.matchedKey === undefined || + typeof item.matchedKey === 'string') && + (item.installation === undefined || + typeof item.installation === 'string'))) { + throw new Error('Invalid JDK cache information retrieved from state.'); + } + return value; +} + + +/***/ }) + +}; diff --git a/dist/setup/971.index.js b/dist/setup/971.index.js index d73a0c05..f83b7ac8 100644 --- a/dist/setup/971.index.js +++ b/dist/setup/971.index.js @@ -6853,11 +6853,13 @@ module.exports = { version: packageJson.version } // EXPORTS __webpack_require__.d(__webpack_exports__, { + Zh: () => (/* binding */ ReserveCacheError), w3: () => (/* binding */ isFeatureAvailable), - P3: () => (/* binding */ restoreCache) + P3: () => (/* binding */ restoreCache), + Io: () => (/* binding */ cache_saveCache) }); -// UNUSED EXPORTS: CACHE_READ_DENIED_PREFIX, CACHE_WRITE_DENIED_PREFIX, CacheReadDeniedError, CacheWriteDeniedError, FinalizeCacheError, ReserveCacheError, ValidationError, saveCache +// UNUSED EXPORTS: CACHE_READ_DENIED_PREFIX, CACHE_WRITE_DENIED_PREFIX, CacheReadDeniedError, CacheWriteDeniedError, FinalizeCacheError, ValidationError // NAMESPACE OBJECT: ./node_modules/@azure/storage-blob/dist/esm/generated/src/models/mappers.js var mappers_namespaceObject = {}; @@ -7045,13 +7047,13 @@ __webpack_require__.d(mappers_namespaceObject, { }); // EXTERNAL MODULE: ./node_modules/@actions/core/lib/core.js + 7 modules -var lib_core = __webpack_require__(3838); +var core = __webpack_require__(3838); // EXTERNAL MODULE: external "path" var external_path_ = __webpack_require__(6928); // EXTERNAL MODULE: ./node_modules/@actions/exec/lib/exec.js + 2 modules var exec = __webpack_require__(5260); // EXTERNAL MODULE: ./node_modules/@actions/glob/lib/glob.js + 17 modules -var lib_glob = __webpack_require__(2377); +var glob = __webpack_require__(2377); // EXTERNAL MODULE: ./node_modules/@actions/io/lib/io.js var io = __webpack_require__(8701); // EXTERNAL MODULE: external "crypto" @@ -7094,7 +7096,7 @@ const GnuTarPathOnWindows = `${process.env['PROGRAMFILES']}\\Git\\usr\\bin\\tar. // The default path of BSDtar on hosted Windows runners const SystemTarPathOnWindows = `${process.env['SYSTEMDRIVE']}\\Windows\\System32\\tar.exe`; const TarFilename = 'cache.tar'; -const constants_ManifestFilename = 'manifest.txt'; +const ManifestFilename = 'manifest.txt'; const CacheFileSizeLimit = 10 * Math.pow(1024, 3); // 10GiB per repository // Prefix the cache backend embeds in a read-denial message (v2 twirp // GetCacheEntryDownloadURL error or the GHES v1 `_apis/artifactcache` 403 body). @@ -7164,7 +7166,7 @@ function resolvePaths(patterns) { var _d; const paths = []; const workspace = (_d = process.env['GITHUB_WORKSPACE']) !== null && _d !== void 0 ? _d : process.cwd(); - const globber = yield glob.create(patterns.join('\n'), { + const globber = yield glob/* create */.v(patterns.join('\n'), { implicitDescendants: false }); try { @@ -7172,10 +7174,9 @@ function resolvePaths(patterns) { _c = _g.value; _e = false; const file = _c; - const relativeFile = path - .relative(workspace, file) - .replace(new RegExp(`\\${path.sep}`, 'g'), '/'); - core.debug(`Matched: ${relativeFile}`); + const relativeFile = external_path_.relative(workspace, file) + .replace(new RegExp(`\\${external_path_.sep}`, 'g'), '/'); + core/* debug */.Yz(`Matched: ${relativeFile}`); // Paths are made relative so the tar entries are all relative to the root of the workspace. if (relativeFile === '') { // path.relative returns empty string if workspace and file are equal @@ -7205,7 +7206,7 @@ function getVersion(app_1) { return __awaiter(this, arguments, void 0, function* (app, additionalArgs = []) { let versionOutput = ''; additionalArgs.push('--version'); - lib_core/* debug */.Yz(`Checking ${app} ${additionalArgs.join(' ')}`); + core/* debug */.Yz(`Checking ${app} ${additionalArgs.join(' ')}`); try { yield exec/* exec */.m(`${app}`, additionalArgs, { ignoreReturnCode: true, @@ -7217,10 +7218,10 @@ function getVersion(app_1) { }); } catch (err) { - lib_core/* debug */.Yz(err.message); + core/* debug */.Yz(err.message); } versionOutput = versionOutput.trim(); - lib_core/* debug */.Yz(versionOutput); + core/* debug */.Yz(versionOutput); return versionOutput; }); } @@ -7229,7 +7230,7 @@ function getCompressionMethod() { return __awaiter(this, void 0, void 0, function* () { const versionOutput = yield getVersion('zstd', ['--quiet']); const version = semver.clean(versionOutput); - lib_core/* debug */.Yz(`zstd version: ${version}`); + core/* debug */.Yz(`zstd version: ${version}`); if (versionOutput === '') { return CompressionMethod.Gzip; } @@ -43242,7 +43243,7 @@ const fsCreateReadStream = external_node_fs_.createReadStream; * A BlobClient represents a URL to an Azure Storage blob; the blob may be a block blob, * append blob, or page blob. */ -class Clients_BlobClient extends StorageClient_StorageClient { +class BlobClient extends StorageClient_StorageClient { /** * blobContext provided by protocol layer. */ @@ -43349,7 +43350,7 @@ class Clients_BlobClient extends StorageClient_StorageClient { * @returns A new BlobClient object identical to the source but with the specified snapshot timestamp */ withSnapshot(snapshot) { - return new Clients_BlobClient(utils_common_setURLParameter(this.url, utils_constants_URLConstants.Parameters.SNAPSHOT, snapshot.length === 0 ? undefined : snapshot), this.pipeline, this.blobClientConfig); + return new BlobClient(utils_common_setURLParameter(this.url, utils_constants_URLConstants.Parameters.SNAPSHOT, snapshot.length === 0 ? undefined : snapshot), this.pipeline, this.blobClientConfig); } /** * Creates a new BlobClient object pointing to a version of this blob. @@ -43359,7 +43360,7 @@ class Clients_BlobClient extends StorageClient_StorageClient { * @returns A new BlobClient object pointing to the version of this blob. */ withVersion(versionId) { - return new Clients_BlobClient(utils_common_setURLParameter(this.url, utils_constants_URLConstants.Parameters.VERSIONID, versionId.length === 0 ? undefined : versionId), this.pipeline, this.blobClientConfig); + return new BlobClient(utils_common_setURLParameter(this.url, utils_constants_URLConstants.Parameters.VERSIONID, versionId.length === 0 ? undefined : versionId), this.pipeline, this.blobClientConfig); } /** * Creates a AppendBlobClient object. @@ -44385,7 +44386,7 @@ class Clients_BlobClient extends StorageClient_StorageClient { /** * AppendBlobClient defines a set of operations applicable to append blobs. */ -class AppendBlobClient extends Clients_BlobClient { +class AppendBlobClient extends BlobClient { /** * appendBlobsContext provided by protocol layer. */ @@ -44687,7 +44688,7 @@ class AppendBlobClient extends Clients_BlobClient { /** * BlockBlobClient defines a set of operations applicable to block blobs. */ -class BlockBlobClient extends Clients_BlobClient { +class BlockBlobClient extends BlobClient { /** * blobContext provided by protocol layer. * @@ -45341,7 +45342,7 @@ class BlockBlobClient extends Clients_BlobClient { /** * PageBlobClient defines a set of operations applicable to page blobs. */ -class PageBlobClient extends Clients_BlobClient { +class PageBlobClient extends BlobClient { /** * pageBlobsContext provided by protocol layer. */ @@ -46409,7 +46410,7 @@ class BlobBatch { url = urlOrBlobClient; credential = credentialOrOptions; } - else if (urlOrBlobClient instanceof Clients_BlobClient) { + else if (urlOrBlobClient instanceof BlobClient) { // Second overload url = urlOrBlobClient.url; credential = urlOrBlobClient.credential; @@ -46427,7 +46428,7 @@ class BlobBatch { url: url, credential: credential, }, async () => { - await new Clients_BlobClient(url, this.batchRequest.createPipeline(credential)).delete(updatedOptions); + await new BlobClient(url, this.batchRequest.createPipeline(credential)).delete(updatedOptions); }); }); } @@ -46444,7 +46445,7 @@ class BlobBatch { credential = credentialOrTier; tier = tierOrOptions; } - else if (urlOrBlobClient instanceof Clients_BlobClient) { + else if (urlOrBlobClient instanceof BlobClient) { // Second overload url = urlOrBlobClient.url; credential = urlOrBlobClient.credential; @@ -46463,7 +46464,7 @@ class BlobBatch { url: url, credential: credential, }, async () => { - await new Clients_BlobClient(url, this.batchRequest.createPipeline(credential)).setAccessTier(tier, updatedOptions); + await new BlobClient(url, this.batchRequest.createPipeline(credential)).setAccessTier(tier, updatedOptions); }); }); } @@ -46969,7 +46970,7 @@ class ContainerClient extends StorageClient_StorageClient { * @returns A new BlobClient object for the given blob name. */ getBlobClient(blobName) { - return new Clients_BlobClient(utils_common_appendToURLPath(this.url, utils_common_EscapePath(blobName)), this.pipeline, this.blobClientConfig); + return new BlobClient(utils_common_appendToURLPath(this.url, utils_common_EscapePath(blobName)), this.pipeline, this.blobClientConfig); } /** * Creates an {@link AppendBlobClient} @@ -49314,7 +49315,7 @@ class FilesNotFoundError extends Error { this.name = 'FilesNotFoundError'; } } -class errors_InvalidResponseError extends Error { +class InvalidResponseError extends Error { constructor(message) { super(message); this.name = 'InvalidResponseError'; @@ -49427,7 +49428,7 @@ class UploadProgress { const uploadSpeed = (transferredBytes / (1024 * 1024) / (elapsedTime / 1000)).toFixed(1); - core.info(`Sent ${transferredBytes} of ${this.contentLength} (${percentage}%), ${uploadSpeed} MBs/sec`); + core/* info */.pq(`Sent ${transferredBytes} of ${this.contentLength} (${percentage}%), ${uploadSpeed} MBs/sec`); if (this.isDone()) { this.displayedComplete = true; } @@ -49477,7 +49478,7 @@ class UploadProgress { * @param options * @returns */ -function uploadUtils_uploadCacheArchiveSDK(signedUploadURL, archivePath, options) { +function uploadCacheArchiveSDK(signedUploadURL, archivePath, options) { return uploadUtils_awaiter(this, void 0, void 0, function* () { var _a; const blobClient = new BlobClient(signedUploadURL); @@ -49492,7 +49493,7 @@ function uploadUtils_uploadCacheArchiveSDK(signedUploadURL, archivePath, options }; try { uploadProgress.startDisplayTimer(); - core.debug(`BlobClient: ${blobClient.name}:${blobClient.accountName}:${blobClient.containerName}`); + core/* debug */.Yz(`BlobClient: ${blobClient.name}:${blobClient.accountName}:${blobClient.containerName}`); const response = yield blockBlobClient.uploadFile(archivePath, uploadOptions); // TODO: better management of non-retryable errors if (response._response.status >= 400) { @@ -49501,7 +49502,7 @@ function uploadUtils_uploadCacheArchiveSDK(signedUploadURL, archivePath, options return response; } catch (error) { - core.warning(`uploadCacheArchiveSDK: internal error uploading cache archive: ${error.message}`); + core/* warning */.$e(`uploadCacheArchiveSDK: internal error uploading cache archive: ${error.message}`); throw error; } finally { @@ -49523,7 +49524,7 @@ var requestUtils_awaiter = (undefined && undefined.__awaiter) || function (thisA -function requestUtils_isSuccessStatusCode(statusCode) { +function isSuccessStatusCode(statusCode) { if (!statusCode) { return false; } @@ -49579,9 +49580,9 @@ function retry(name_1, method_1, getStatusCode_1) { isRetryable = isRetryableStatusCode(statusCode); errorMessage = `Cache service responded with ${statusCode}`; } - lib_core/* debug */.Yz(`${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}`); + core/* debug */.Yz(`${name} - Attempt ${attempt} of ${maxAttempts} failed with error: ${errorMessage}`); if (!isRetryable) { - lib_core/* debug */.Yz(`${name} - Error is not retryable`); + core/* debug */.Yz(`${name} - Error is not retryable`); break; } yield sleep(delay); @@ -49590,7 +49591,7 @@ function retry(name_1, method_1, getStatusCode_1) { throw Error(`${name} failed: ${errorMessage}`); }); } -function requestUtils_retryTypedResponse(name_1, method_1) { +function retryTypedResponse(name_1, method_1) { return requestUtils_awaiter(this, arguments, void 0, function* (name, method, maxAttempts = DefaultRetryAttempts, delay = DefaultRetryDelay) { return yield retry(name, method, (response) => response.statusCode, maxAttempts, delay, // If the error object contains the statusCode property, extract it and return @@ -49610,7 +49611,7 @@ function requestUtils_retryTypedResponse(name_1, method_1) { }); }); } -function requestUtils_retryHttpClientResponse(name_1, method_1) { +function retryHttpClientResponse(name_1, method_1) { return requestUtils_awaiter(this, arguments, void 0, function* (name, method, maxAttempts = DefaultRetryAttempts, delay = DefaultRetryDelay) { return yield retry(name, method, (response) => response.message.statusCode, maxAttempts, delay); }); @@ -49672,7 +49673,7 @@ class DownloadProgress { this.segmentIndex = this.segmentIndex + 1; this.segmentSize = segmentSize; this.receivedBytes = 0; - lib_core/* debug */.Yz(`Downloading segment at offset ${this.segmentOffset} with length ${this.segmentSize}...`); + core/* debug */.Yz(`Downloading segment at offset ${this.segmentOffset} with length ${this.segmentSize}...`); } /** * Sets the number of bytes received for the current segment. @@ -49708,7 +49709,7 @@ class DownloadProgress { const downloadSpeed = (transferredBytes / (1024 * 1024) / (elapsedTime / 1000)).toFixed(1); - lib_core/* info */.pq(`Received ${transferredBytes} of ${this.contentLength} (${percentage}%), ${downloadSpeed} MBs/sec`); + core/* info */.pq(`Received ${transferredBytes} of ${this.contentLength} (${percentage}%), ${downloadSpeed} MBs/sec`); if (this.isDone()) { this.displayedComplete = true; } @@ -49758,11 +49759,11 @@ function downloadCacheHttpClient(archiveLocation, archivePath) { return downloadUtils_awaiter(this, void 0, void 0, function* () { const writeStream = external_fs_.createWriteStream(archivePath); const httpClient = new lib/* HttpClient */.Qq('actions/cache'); - const downloadResponse = yield requestUtils_retryHttpClientResponse('downloadCache', () => downloadUtils_awaiter(this, void 0, void 0, function* () { return httpClient.get(archiveLocation); })); + const downloadResponse = yield retryHttpClientResponse('downloadCache', () => downloadUtils_awaiter(this, void 0, void 0, function* () { return httpClient.get(archiveLocation); })); // Abort download if no traffic received over the socket. downloadResponse.message.socket.setTimeout(SocketTimeout, () => { downloadResponse.message.destroy(); - lib_core/* debug */.Yz(`Aborting download, socket timed out after ${SocketTimeout} ms`); + core/* debug */.Yz(`Aborting download, socket timed out after ${SocketTimeout} ms`); }); yield pipeResponseToStream(downloadResponse, writeStream); // Validate download size. @@ -49775,7 +49776,7 @@ function downloadCacheHttpClient(archiveLocation, archivePath) { } } else { - lib_core/* debug */.Yz('Unable to validate download, no Content-Length header'); + core/* debug */.Yz('Unable to validate download, no Content-Length header'); } }); } @@ -49794,7 +49795,7 @@ function downloadCacheHttpClientConcurrent(archiveLocation, archivePath, options keepAlive: true }); try { - const res = yield requestUtils_retryHttpClientResponse('downloadCacheMetadata', () => downloadUtils_awaiter(this, void 0, void 0, function* () { return yield httpClient.request('HEAD', archiveLocation, null, {}); })); + const res = yield retryHttpClientResponse('downloadCacheMetadata', () => downloadUtils_awaiter(this, void 0, void 0, function* () { return yield httpClient.request('HEAD', archiveLocation, null, {}); })); const lengthHeader = res.message.headers['content-length']; if (lengthHeader === undefined || lengthHeader === null) { throw new Error('Content-Length not found on blob response'); @@ -49872,7 +49873,7 @@ function downloadSegmentRetry(httpClient, archiveLocation, offset, count) { } function downloadSegment(httpClient, archiveLocation, offset, count) { return downloadUtils_awaiter(this, void 0, void 0, function* () { - const partRes = yield requestUtils_retryHttpClientResponse('downloadCachePart', () => downloadUtils_awaiter(this, void 0, void 0, function* () { + const partRes = yield retryHttpClientResponse('downloadCachePart', () => downloadUtils_awaiter(this, void 0, void 0, function* () { return yield httpClient.get(archiveLocation, { Range: `bytes=${offset}-${offset + count - 1}` }); @@ -49910,7 +49911,7 @@ function downloadCacheStorageSDK(archiveLocation, archivePath, options) { if (contentLength < 0) { // We should never hit this condition, but just in case fall back to downloading the // file as one large stream - lib_core/* debug */.Yz('Unable to determine content length, downloading file with http-client...'); + core/* debug */.Yz('Unable to determine content length, downloading file with http-client...'); yield downloadCacheHttpClient(archiveLocation, archivePath); } else { @@ -49971,7 +49972,7 @@ const promiseWithTimeout = (timeoutMs, promise) => downloadUtils_awaiter(void 0, * * @param copy the original upload options */ -function options_getUploadOptions(copy) { +function getUploadOptions(copy) { // Defaults if not overriden const result = { useAzureSdk: false, @@ -50000,9 +50001,9 @@ function options_getUploadOptions(copy) { result.uploadChunkSize = !isNaN(Number(process.env['CACHE_UPLOAD_CHUNK_SIZE'])) ? Math.min(128 * 1024 * 1024, Number(process.env['CACHE_UPLOAD_CHUNK_SIZE']) * 1024 * 1024) : result.uploadChunkSize; - core.debug(`Use Azure SDK: ${result.useAzureSdk}`); - core.debug(`Upload concurrency: ${result.uploadConcurrency}`); - core.debug(`Upload chunk size: ${result.uploadChunkSize}`); + core/* debug */.Yz(`Use Azure SDK: ${result.useAzureSdk}`); + core/* debug */.Yz(`Upload concurrency: ${result.uploadConcurrency}`); + core/* debug */.Yz(`Upload chunk size: ${result.uploadChunkSize}`); return result; } /** @@ -50045,17 +50046,17 @@ function getDownloadOptions(copy) { isFinite(Number(segmentDownloadTimeoutMins))) { result.segmentTimeoutInMs = Number(segmentDownloadTimeoutMins) * 60 * 1000; } - lib_core/* debug */.Yz(`Use Azure SDK: ${result.useAzureSdk}`); - lib_core/* debug */.Yz(`Download concurrency: ${result.downloadConcurrency}`); - lib_core/* debug */.Yz(`Request timeout (ms): ${result.timeoutInMs}`); - lib_core/* debug */.Yz(`Cache segment download timeout mins env var: ${process.env['SEGMENT_DOWNLOAD_TIMEOUT_MINS']}`); - lib_core/* debug */.Yz(`Segment download timeout (ms): ${result.segmentTimeoutInMs}`); - lib_core/* debug */.Yz(`Lookup only: ${result.lookupOnly}`); + core/* debug */.Yz(`Use Azure SDK: ${result.useAzureSdk}`); + core/* debug */.Yz(`Download concurrency: ${result.downloadConcurrency}`); + core/* debug */.Yz(`Request timeout (ms): ${result.timeoutInMs}`); + core/* debug */.Yz(`Cache segment download timeout mins env var: ${process.env['SEGMENT_DOWNLOAD_TIMEOUT_MINS']}`); + core/* debug */.Yz(`Segment download timeout (ms): ${result.segmentTimeoutInMs}`); + core/* debug */.Yz(`Lookup only: ${result.lookupOnly}`); return result; } //# sourceMappingURL=options.js.map ;// CONCATENATED MODULE: ./node_modules/@actions/cache/lib/internal/config.js -function config_isGhes() { +function isGhes() { const ghUrl = new URL(process.env['GITHUB_SERVER_URL'] || 'https://github.com'); const hostname = ghUrl.hostname.trimEnd().toUpperCase(); const isGitHubHost = hostname === 'GITHUB.COM'; @@ -50063,10 +50064,10 @@ function config_isGhes() { const isLocalHost = hostname.endsWith('.LOCALHOST'); return !isGitHubHost && !isGheHost && !isLocalHost; } -function config_getCacheServiceVersion() { +function getCacheServiceVersion() { // Cache service v2 is not supported on GHES. We will default to // cache service v1 even if the feature flag was enabled by user. - if (config_isGhes()) + if (isGhes()) return 'v1'; return process.env['ACTIONS_CACHE_SERVICE_V2'] ? 'v2' : 'v1'; } @@ -50074,7 +50075,7 @@ function config_getCacheServiceVersion() { // write-only}, none = neither. const KNOWN_CACHE_MODES = ['none', 'read', 'write', 'write-only']; // The effective cache-mode exported by the runner, or '' when not set. -function config_getCacheMode() { +function getCacheMode() { return (process.env['ACTIONS_CACHE_MODE'] || '').trim().toLowerCase(); } // Unset or unrecognized modes are permissive so behavior matches today. @@ -50083,13 +50084,13 @@ function isCacheReadable(mode) { return true; return mode === 'read' || mode === 'write'; } -function config_isCacheWritable(mode) { +function isCacheWritable(mode) { if (!KNOWN_CACHE_MODES.includes(mode)) return true; return mode === 'write' || mode === 'write-only'; } function getCacheServiceURL() { - const version = config_getCacheServiceVersion(); + const version = getCacheServiceVersion(); // Based on the version of the cache service, we will determine which // URL to use. switch (version) { @@ -50144,7 +50145,7 @@ function getCacheApiUrl(resource) { throw new Error('Cache Service Url not found, unable to restore cache.'); } const url = `${baseUrl}_apis/artifactcache/${resource}`; - lib_core/* debug */.Yz(`Resource Url: ${url}`); + core/* debug */.Yz(`Resource Url: ${url}`); return url; } function createAcceptHeader(type, apiVersion) { @@ -50169,16 +50170,16 @@ function getCacheEntry(keys, paths, options) { const httpClient = createHttpClient(); const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive); const resource = `cache?keys=${encodeURIComponent(keys.join(','))}&version=${version}`; - const response = yield requestUtils_retryTypedResponse('getCacheEntry', () => cacheHttpClient_awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); })); + const response = yield retryTypedResponse('getCacheEntry', () => cacheHttpClient_awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); })); // Cache not found if (response.statusCode === 204) { // List cache for primary key only if cache miss occurs - if (lib_core/* isDebug */._o()) { + if (core/* isDebug */._o()) { yield printCachesListForDiagnostics(keys[0], httpClient, version); } return null; } - if (!requestUtils_isSuccessStatusCode(response.statusCode)) { + if (!isSuccessStatusCode(response.statusCode)) { // Only surface the receiver's body for a `cache read denied:` policy denial // so callers can dispatch on it; keep the generic message otherwise. const errorMessage = (_a = response.error) === null || _a === void 0 ? void 0 : _a.message; @@ -50193,23 +50194,23 @@ function getCacheEntry(keys, paths, options) { // Cache achiveLocation not found. This should never happen, and hence bail out. throw new Error('Cache not found.'); } - lib_core/* setSecret */.Pq(cacheDownloadUrl); - lib_core/* debug */.Yz(`Cache Result:`); - lib_core/* debug */.Yz(JSON.stringify(cacheResult)); + core/* setSecret */.Pq(cacheDownloadUrl); + core/* debug */.Yz(`Cache Result:`); + core/* debug */.Yz(JSON.stringify(cacheResult)); return cacheResult; }); } function printCachesListForDiagnostics(key, httpClient, version) { return cacheHttpClient_awaiter(this, void 0, void 0, function* () { const resource = `caches?key=${encodeURIComponent(key)}`; - const response = yield requestUtils_retryTypedResponse('listCache', () => cacheHttpClient_awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); })); + const response = yield retryTypedResponse('listCache', () => cacheHttpClient_awaiter(this, void 0, void 0, function* () { return httpClient.getJson(getCacheApiUrl(resource)); })); if (response.statusCode === 200) { const cacheListResult = response.result; const totalCount = cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.totalCount; if (totalCount && totalCount > 0) { - lib_core/* debug */.Yz(`No matching cache found for cache key '${key}', version '${version} and scope ${process.env['GITHUB_REF']}. There exist one or more cache(s) with similar key but they have different version or scope. See more info on cache matching here: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key \nOther caches with similar key:`); + core/* debug */.Yz(`No matching cache found for cache key '${key}', version '${version} and scope ${process.env['GITHUB_REF']}. There exist one or more cache(s) with similar key but they have different version or scope. See more info on cache matching here: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key \nOther caches with similar key:`); for (const cacheEntry of (cacheListResult === null || cacheListResult === void 0 ? void 0 : cacheListResult.artifactCaches) || []) { - lib_core/* debug */.Yz(`Cache Key: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheKey}, Cache Version: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheVersion}, Cache Scope: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.scope}, Cache Created: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.creationTime}`); + core/* debug */.Yz(`Cache Key: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheKey}, Cache Version: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.cacheVersion}, Cache Scope: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.scope}, Cache Created: ${cacheEntry === null || cacheEntry === void 0 ? void 0 : cacheEntry.creationTime}`); } } } @@ -50242,7 +50243,7 @@ function downloadCache(archiveLocation, archivePath, options) { function reserveCache(key, paths, options) { return cacheHttpClient_awaiter(this, void 0, void 0, function* () { const httpClient = createHttpClient(); - const version = utils.getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive); + const version = getCacheVersion(paths, options === null || options === void 0 ? void 0 : options.compressionMethod, options === null || options === void 0 ? void 0 : options.enableCrossOsArchive); const reserveCacheRequest = { key, version, @@ -50264,7 +50265,7 @@ function getContentRange(start, end) { } function uploadChunk(httpClient, resourceUrl, openStream, start, end) { return cacheHttpClient_awaiter(this, void 0, void 0, function* () { - core.debug(`Uploading chunk of size ${end - start + 1} bytes at offset ${start} with content range: ${getContentRange(start, end)}`); + core/* debug */.Yz(`Uploading chunk of size ${end - start + 1} bytes at offset ${start} with content range: ${getContentRange(start, end)}`); const additionalHeaders = { 'Content-Type': 'application/octet-stream', 'Content-Range': getContentRange(start, end) @@ -50280,14 +50281,14 @@ function uploadChunk(httpClient, resourceUrl, openStream, start, end) { function uploadFile(httpClient, cacheId, archivePath, options) { return cacheHttpClient_awaiter(this, void 0, void 0, function* () { // Upload Chunks - const fileSize = utils.getArchiveFileSizeInBytes(archivePath); + const fileSize = getArchiveFileSizeInBytes(archivePath); const resourceUrl = getCacheApiUrl(`caches/${cacheId.toString()}`); - const fd = fs.openSync(archivePath, 'r'); + const fd = external_fs_.openSync(archivePath, 'r'); const uploadOptions = getUploadOptions(options); - const concurrency = utils.assertDefined('uploadConcurrency', uploadOptions.uploadConcurrency); - const maxChunkSize = utils.assertDefined('uploadChunkSize', uploadOptions.uploadChunkSize); + const concurrency = assertDefined('uploadConcurrency', uploadOptions.uploadConcurrency); + const maxChunkSize = assertDefined('uploadChunkSize', uploadOptions.uploadChunkSize); const parallelUploads = [...new Array(concurrency).keys()]; - core.debug('Awaiting all uploads'); + core/* debug */.Yz('Awaiting all uploads'); let offset = 0; try { yield Promise.all(parallelUploads.map(() => cacheHttpClient_awaiter(this, void 0, void 0, function* () { @@ -50296,8 +50297,7 @@ function uploadFile(httpClient, cacheId, archivePath, options) { const start = offset; const end = offset + chunkSize - 1; offset += maxChunkSize; - yield uploadChunk(httpClient, resourceUrl, () => fs - .createReadStream(archivePath, { + yield uploadChunk(httpClient, resourceUrl, () => external_fs_.createReadStream(archivePath, { fd, start, end, @@ -50310,7 +50310,7 @@ function uploadFile(httpClient, cacheId, archivePath, options) { }))); } finally { - fs.closeSync(fd); + external_fs_.closeSync(fd); } return; }); @@ -50335,17 +50335,17 @@ function saveCache(cacheId, archivePath, signedUploadURL, options) { } else { const httpClient = createHttpClient(); - core.debug('Upload cache'); + core/* debug */.Yz('Upload cache'); yield uploadFile(httpClient, cacheId, archivePath, options); // Commit Cache - core.debug('Commiting cache'); - const cacheSize = utils.getArchiveFileSizeInBytes(archivePath); - core.info(`Cache Size: ~${Math.round(cacheSize / (1024 * 1024))} MB (${cacheSize} B)`); + core/* debug */.Yz('Commiting cache'); + const cacheSize = getArchiveFileSizeInBytes(archivePath); + core/* info */.pq(`Cache Size: ~${Math.round(cacheSize / (1024 * 1024))} MB (${cacheSize} B)`); const commitCacheResponse = yield commitCache(httpClient, cacheId, cacheSize); if (!isSuccessStatusCode(commitCacheResponse.statusCode)) { throw new Error(`Cache service responded with ${commitCacheResponse.statusCode} during commit cache.`); } - core.info('Cache saved successfully'); + core/* info */.pq('Cache saved successfully'); } }); } @@ -50967,12 +50967,12 @@ function maskSigUrl(url) { const parsedUrl = new URL(url); const signature = parsedUrl.searchParams.get('sig'); if (signature) { - (0,lib_core/* setSecret */.Pq)(signature); - (0,lib_core/* setSecret */.Pq)(encodeURIComponent(signature)); + (0,core/* setSecret */.Pq)(signature); + (0,core/* setSecret */.Pq)(encodeURIComponent(signature)); } } catch (error) { - (0,lib_core/* debug */.Yz)(`Failed to parse URL: ${url} ${error instanceof Error ? error.message : String(error)}`); + (0,core/* debug */.Yz)(`Failed to parse URL: ${url} ${error instanceof Error ? error.message : String(error)}`); } } /** @@ -50998,7 +50998,7 @@ function maskSigUrl(url) { */ function maskSecretUrls(body) { if (typeof body !== 'object' || body === null) { - (0,lib_core/* debug */.Yz)('body is not an object or is null'); + (0,core/* debug */.Yz)('body is not an object or is null'); return; } if ('signed_upload_url' in body && @@ -51062,7 +51062,7 @@ class CacheServiceClient { request(service, method, contentType, data) { return cacheTwirpClient_awaiter(this, void 0, void 0, function* () { const url = new URL(`/twirp/${service}/${method}`, this.baseUrl).href; - (0,lib_core/* debug */.Yz)(`[Request] ${method} ${url}`); + (0,core/* debug */.Yz)(`[Request] ${method} ${url}`); const headers = { 'Content-Type': contentType }; @@ -51086,11 +51086,11 @@ class CacheServiceClient { const response = yield operation(); const statusCode = response.message.statusCode; rawBody = yield response.readBody(); - (0,lib_core/* debug */.Yz)(`[Response] - ${response.message.statusCode}`); - (0,lib_core/* debug */.Yz)(`Headers: ${JSON.stringify(response.message.headers, null, 2)}`); + (0,core/* debug */.Yz)(`[Response] - ${response.message.statusCode}`); + (0,core/* debug */.Yz)(`Headers: ${JSON.stringify(response.message.headers, null, 2)}`); const body = JSON.parse(rawBody); maskSecretUrls(body); - (0,lib_core/* debug */.Yz)(`Body: ${JSON.stringify(body, null, 2)}`); + (0,core/* debug */.Yz)(`Body: ${JSON.stringify(body, null, 2)}`); if (this.isSuccessStatusCode(statusCode)) { return { response, body }; } @@ -51109,7 +51109,7 @@ class CacheServiceClient { if (retryAfterHeader) { const parsedSeconds = parseInt(retryAfterHeader, 10); if (!isNaN(parsedSeconds) && parsedSeconds > 0) { - (0,lib_core/* warning */.$e)(`You've hit a rate limit, your rate limit will reset in ${parsedSeconds} seconds`); + (0,core/* warning */.$e)(`You've hit a rate limit, your rate limit will reset in ${parsedSeconds} seconds`); } } throw new RateLimitError(`Rate limited: ${errorMessage}`); @@ -51117,7 +51117,7 @@ class CacheServiceClient { } catch (error) { if (error instanceof SyntaxError) { - (0,lib_core/* debug */.Yz)(`Raw Body: ${rawBody}`); + (0,core/* debug */.Yz)(`Raw Body: ${rawBody}`); } if (error instanceof UsageError) { throw error; @@ -51138,7 +51138,7 @@ class CacheServiceClient { throw new Error(`Failed to make request after ${this.maxAttempts} attempts: ${errorMessage}`); } const retryTimeMilliseconds = this.getExponentialRetryTimeMilliseconds(attempt); - (0,lib_core/* info */.pq)(`Attempt ${attempt + 1} of ${this.maxAttempts} failed with error: ${errorMessage}. Retrying request in ${retryTimeMilliseconds} ms...`); + (0,core/* info */.pq)(`Attempt ${attempt + 1} of ${this.maxAttempts} failed with error: ${errorMessage}. Retrying request in ${retryTimeMilliseconds} ms...`); yield this.sleep(retryTimeMilliseconds); attempt++; } @@ -51258,7 +51258,7 @@ function getTarArgs(tarPath_1, compressionMethod_1, type_1) { ? tarFile : cacheFileName.replace(new RegExp(`\\${external_path_.sep}`, 'g'), '/'), '--exclude', BSD_TAR_ZSTD ? tarFile - : cacheFileName.replace(new RegExp(`\\${external_path_.sep}`, 'g'), '/'), '-P', '-C', workingDirectory.replace(new RegExp(`\\${external_path_.sep}`, 'g'), '/'), '--files-from', constants_ManifestFilename); + : cacheFileName.replace(new RegExp(`\\${external_path_.sep}`, 'g'), '/'), '-P', '-C', workingDirectory.replace(new RegExp(`\\${external_path_.sep}`, 'g'), '/'), '--files-from', ManifestFilename); break; case 'extract': args.push('-xf', BSD_TAR_ZSTD @@ -51402,7 +51402,7 @@ function execCommands(commands, cwd) { }); } // List the contents of a tar -function tar_listTar(archivePath, compressionMethod) { +function listTar(archivePath, compressionMethod) { return tar_awaiter(this, void 0, void 0, function* () { const commands = yield getCommands(compressionMethod, 'list', archivePath); yield execCommands(commands); @@ -51419,10 +51419,10 @@ function extractTar(archivePath, compressionMethod) { }); } // Create a tar -function tar_createTar(archiveFolder, sourceDirectories, compressionMethod) { +function createTar(archiveFolder, sourceDirectories, compressionMethod) { return tar_awaiter(this, void 0, void 0, function* () { // Write source directories to manifest.txt to avoid command length limits - writeFileSync(path.join(archiveFolder, ManifestFilename), sourceDirectories.join('\n')); + (0,external_fs_.writeFileSync)(external_path_.join(archiveFolder, ManifestFilename), sourceDirectories.join('\n')); const commands = yield getCommands(compressionMethod, 'create'); yield execCommands(commands, archiveFolder); }); @@ -51531,7 +51531,7 @@ function checkKey(key) { * @returns boolean return true if Actions cache service feature is available, otherwise false */ function isFeatureAvailable() { - const cacheServiceVersion = config_getCacheServiceVersion(); + const cacheServiceVersion = getCacheServiceVersion(); // Check availability based on cache service version switch (cacheServiceVersion) { case 'v2': @@ -51555,13 +51555,13 @@ function isFeatureAvailable() { */ function restoreCache(paths_1, primaryKey_1, restoreKeys_1, options_1) { return cache_awaiter(this, arguments, void 0, function* (paths, primaryKey, restoreKeys, options, enableCrossOsArchive = false) { - const cacheServiceVersion = config_getCacheServiceVersion(); - lib_core/* debug */.Yz(`Cache service version: ${cacheServiceVersion}`); + const cacheServiceVersion = getCacheServiceVersion(); + core/* debug */.Yz(`Cache service version: ${cacheServiceVersion}`); checkPaths(paths); - const cacheMode = config_getCacheMode(); + const cacheMode = getCacheMode(); if (!isCacheReadable(cacheMode)) { - lib_core/* info */.pq(`Cache restore skipped: the effective cache-mode '${cacheMode}' does not permit reads.`); - lib_core/* debug */.Yz(`Skipped restore for paths [${paths.join(', ')}] with primary key '${primaryKey}'.`); + core/* info */.pq(`Cache restore skipped: the effective cache-mode '${cacheMode}' does not permit reads.`); + core/* debug */.Yz(`Skipped restore for paths [${paths.join(', ')}] with primary key '${primaryKey}'.`); return undefined; } switch (cacheServiceVersion) { @@ -51588,8 +51588,8 @@ function restoreCacheV1(paths_1, primaryKey_1, restoreKeys_1, options_1) { var _a; restoreKeys = restoreKeys || []; const keys = [primaryKey, ...restoreKeys]; - lib_core/* debug */.Yz('Resolved Keys:'); - lib_core/* debug */.Yz(JSON.stringify(keys)); + core/* debug */.Yz('Resolved Keys:'); + core/* debug */.Yz(JSON.stringify(keys)); if (keys.length > 10) { throw new ValidationError(`Key Validation Error: Keys are limited to a maximum of 10.`); } @@ -51625,20 +51625,20 @@ function restoreCacheV1(paths_1, primaryKey_1, restoreKeys_1, options_1) { return undefined; } if (options === null || options === void 0 ? void 0 : options.lookupOnly) { - lib_core/* info */.pq('Lookup only - skipping download'); + core/* info */.pq('Lookup only - skipping download'); return cacheEntry.cacheKey; } archivePath = external_path_.join(yield createTempDirectory(), getCacheFileName(compressionMethod)); - lib_core/* debug */.Yz(`Archive Path: ${archivePath}`); + core/* debug */.Yz(`Archive Path: ${archivePath}`); // Download the cache from the cache entry yield downloadCache(cacheEntry.archiveLocation, archivePath, options); - if (lib_core/* isDebug */._o()) { - yield tar_listTar(archivePath, compressionMethod); + if (core/* isDebug */._o()) { + yield listTar(archivePath, compressionMethod); } const archiveFileSize = getArchiveFileSizeInBytes(archivePath); - lib_core/* info */.pq(`Cache Size: ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B)`); + core/* info */.pq(`Cache Size: ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B)`); yield extractTar(archivePath, compressionMethod); - lib_core/* info */.pq('Cache restored successfully'); + core/* info */.pq('Cache restored successfully'); return cacheEntry.cacheKey; } catch (error) { @@ -51654,10 +51654,10 @@ function restoreCacheV1(paths_1, primaryKey_1, restoreKeys_1, options_1) { if (typedError instanceof lib/* HttpClientError */.Kg && typeof typedError.statusCode === 'number' && typedError.statusCode >= 500) { - lib_core/* error */.z3(`Failed to restore: ${error.message}`); + core/* error */.z3(`Failed to restore: ${error.message}`); } else { - lib_core/* warning */.$e(`Failed to restore: ${error.message}`); + core/* warning */.$e(`Failed to restore: ${error.message}`); } } } @@ -51667,7 +51667,7 @@ function restoreCacheV1(paths_1, primaryKey_1, restoreKeys_1, options_1) { yield unlinkFile(archivePath); } catch (error) { - lib_core/* debug */.Yz(`Failed to delete archive: ${error}`); + core/* debug */.Yz(`Failed to delete archive: ${error}`); } } return undefined; @@ -51690,8 +51690,8 @@ function restoreCacheV2(paths_1, primaryKey_1, restoreKeys_1, options_1) { options = Object.assign(Object.assign({}, options), { useAzureSdk: true }); restoreKeys = restoreKeys || []; const keys = [primaryKey, ...restoreKeys]; - lib_core/* debug */.Yz('Resolved Keys:'); - lib_core/* debug */.Yz(JSON.stringify(keys)); + core/* debug */.Yz('Resolved Keys:'); + core/* debug */.Yz(JSON.stringify(keys)); if (keys.length > 10) { throw new ValidationError(`Key Validation Error: Keys are limited to a maximum of 10.`); } @@ -51722,31 +51722,31 @@ function restoreCacheV2(paths_1, primaryKey_1, restoreKeys_1, options_1) { throw error; } if (!response.ok) { - lib_core/* debug */.Yz(`Cache not found for version ${request.version} of keys: ${keys.join(', ')}`); + core/* debug */.Yz(`Cache not found for version ${request.version} of keys: ${keys.join(', ')}`); return undefined; } const isRestoreKeyMatch = request.key !== response.matchedKey; if (isRestoreKeyMatch) { - lib_core/* info */.pq(`Cache hit for restore-key: ${response.matchedKey}`); + core/* info */.pq(`Cache hit for restore-key: ${response.matchedKey}`); } else { - lib_core/* info */.pq(`Cache hit for: ${response.matchedKey}`); + core/* info */.pq(`Cache hit for: ${response.matchedKey}`); } if (options === null || options === void 0 ? void 0 : options.lookupOnly) { - lib_core/* info */.pq('Lookup only - skipping download'); + core/* info */.pq('Lookup only - skipping download'); return response.matchedKey; } archivePath = external_path_.join(yield createTempDirectory(), getCacheFileName(compressionMethod)); - lib_core/* debug */.Yz(`Archive path: ${archivePath}`); - lib_core/* debug */.Yz(`Starting download of archive to: ${archivePath}`); + core/* debug */.Yz(`Archive path: ${archivePath}`); + core/* debug */.Yz(`Starting download of archive to: ${archivePath}`); yield downloadCache(response.signedDownloadUrl, archivePath, options); const archiveFileSize = getArchiveFileSizeInBytes(archivePath); - lib_core/* info */.pq(`Cache Size: ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B)`); - if (lib_core/* isDebug */._o()) { - yield tar_listTar(archivePath, compressionMethod); + core/* info */.pq(`Cache Size: ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B)`); + if (core/* isDebug */._o()) { + yield listTar(archivePath, compressionMethod); } yield extractTar(archivePath, compressionMethod); - lib_core/* info */.pq('Cache restored successfully'); + core/* info */.pq('Cache restored successfully'); return response.matchedKey; } catch (error) { @@ -51762,10 +51762,10 @@ function restoreCacheV2(paths_1, primaryKey_1, restoreKeys_1, options_1) { if (typedError instanceof lib/* HttpClientError */.Kg && typeof typedError.statusCode === 'number' && typedError.statusCode >= 500) { - lib_core/* error */.z3(`Failed to restore: ${error.message}`); + core/* error */.z3(`Failed to restore: ${error.message}`); } else { - lib_core/* warning */.$e(`Failed to restore: ${error.message}`); + core/* warning */.$e(`Failed to restore: ${error.message}`); } } } @@ -51776,7 +51776,7 @@ function restoreCacheV2(paths_1, primaryKey_1, restoreKeys_1, options_1) { } } catch (error) { - lib_core/* debug */.Yz(`Failed to delete archive: ${error}`); + core/* debug */.Yz(`Failed to delete archive: ${error}`); } } return undefined; @@ -51794,13 +51794,13 @@ function restoreCacheV2(paths_1, primaryKey_1, restoreKeys_1, options_1) { function cache_saveCache(paths_1, key_1, options_1) { return cache_awaiter(this, arguments, void 0, function* (paths, key, options, enableCrossOsArchive = false) { const cacheServiceVersion = getCacheServiceVersion(); - core.debug(`Cache service version: ${cacheServiceVersion}`); + core/* debug */.Yz(`Cache service version: ${cacheServiceVersion}`); checkPaths(paths); checkKey(key); const cacheMode = getCacheMode(); if (!isCacheWritable(cacheMode)) { - core.info(`Cache save skipped: the effective cache-mode '${cacheMode}' does not permit writes.`); - core.debug(`Skipped save for paths [${paths.join(', ')}] with key '${key}'.`); + core/* info */.pq(`Cache save skipped: the effective cache-mode '${cacheMode}' does not permit writes.`); + core/* debug */.Yz(`Skipped save for paths [${paths.join(', ')}] with key '${key}'.`); return -1; } switch (cacheServiceVersion) { @@ -51824,31 +51824,31 @@ function cache_saveCache(paths_1, key_1, options_1) { function saveCacheV1(paths_1, key_1, options_1) { return cache_awaiter(this, arguments, void 0, function* (paths, key, options, enableCrossOsArchive = false) { var _a, _b, _c, _d, _e, _f; - const compressionMethod = yield utils.getCompressionMethod(); + const compressionMethod = yield getCompressionMethod(); let cacheId = -1; - const cachePaths = yield utils.resolvePaths(paths); - core.debug('Cache Paths:'); - core.debug(`${JSON.stringify(cachePaths)}`); + const cachePaths = yield resolvePaths(paths); + core/* debug */.Yz('Cache Paths:'); + core/* debug */.Yz(`${JSON.stringify(cachePaths)}`); if (cachePaths.length === 0) { throw new Error(`Path Validation Error: Path(s) specified in the action for caching do(es) not exist, hence no cache is being saved.`); } - const archiveFolder = yield utils.createTempDirectory(); - const archivePath = path.join(archiveFolder, utils.getCacheFileName(compressionMethod)); - core.debug(`Archive Path: ${archivePath}`); + const archiveFolder = yield createTempDirectory(); + const archivePath = external_path_.join(archiveFolder, getCacheFileName(compressionMethod)); + core/* debug */.Yz(`Archive Path: ${archivePath}`); try { yield createTar(archiveFolder, cachePaths, compressionMethod); - if (core.isDebug()) { + if (core/* isDebug */._o()) { yield listTar(archivePath, compressionMethod); } const fileSizeLimit = 10 * 1024 * 1024 * 1024; // 10GB per repo limit - const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); - core.debug(`File Size: ${archiveFileSize}`); + const archiveFileSize = getArchiveFileSizeInBytes(archivePath); + core/* debug */.Yz(`File Size: ${archiveFileSize}`); // For GHES, this check will take place in ReserveCache API with enterprise file size limit if (archiveFileSize > fileSizeLimit && !isGhes()) { throw new Error(`Cache size of ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B) is over the 10GB limit, not saving cache.`); } - core.debug('Reserving Cache'); - const reserveCacheResponse = yield cacheHttpClient.reserveCache(key, paths, { + core/* debug */.Yz('Reserving Cache'); + const reserveCacheResponse = yield reserveCache(key, paths, { compressionMethod, enableCrossOsArchive, cacheSize: archiveFileSize @@ -51872,8 +51872,8 @@ function saveCacheV1(paths_1, key_1, options_1) { } throw new ReserveCacheError(`Unable to reserve cache with key ${key}, another job may be creating this cache. More details: ${(_f = reserveCacheResponse === null || reserveCacheResponse === void 0 ? void 0 : reserveCacheResponse.error) === null || _f === void 0 ? void 0 : _f.message}`); } - core.debug(`Saving Cache (ID: ${cacheId})`); - yield cacheHttpClient.saveCache(cacheId, archivePath, '', options); + core/* debug */.Yz(`Saving Cache (ID: ${cacheId})`); + yield saveCache(cacheId, archivePath, '', options); } catch (error) { const typedError = error; @@ -51881,30 +51881,30 @@ function saveCacheV1(paths_1, key_1, options_1) { throw error; } else if (typedError.name === ReserveCacheError.name) { - core.info(`Failed to save: ${typedError.message}`); + core/* info */.pq(`Failed to save: ${typedError.message}`); } else { // Log server errors (5xx) as errors, all other errors as warnings. // A write denied by policy (CacheWriteDeniedError) is not an // HttpClientError and its name does not match the ReserveCacheError arm, // so it falls here and is warned without failing the run. - if (typedError instanceof HttpClientError && + if (typedError instanceof lib/* HttpClientError */.Kg && typeof typedError.statusCode === 'number' && typedError.statusCode >= 500) { - core.error(`Failed to save: ${typedError.message}`); + core/* error */.z3(`Failed to save: ${typedError.message}`); } else { - core.warning(`Failed to save: ${typedError.message}`); + core/* warning */.$e(`Failed to save: ${typedError.message}`); } } } finally { // Try to delete the archive to save space try { - yield utils.unlinkFile(archivePath); + yield unlinkFile(archivePath); } catch (error) { - core.debug(`Failed to delete archive: ${error}`); + core/* debug */.Yz(`Failed to delete archive: ${error}`); } } return cacheId; @@ -51926,29 +51926,29 @@ function saveCacheV2(paths_1, key_1, options_1) { // ...options goes first because we want to override the default values // set in UploadOptions with these specific figures options = Object.assign(Object.assign({}, options), { uploadChunkSize: 64 * 1024 * 1024, uploadConcurrency: 8, useAzureSdk: true }); - const compressionMethod = yield utils.getCompressionMethod(); - const twirpClient = cacheTwirpClient.internalCacheTwirpClient(); + const compressionMethod = yield getCompressionMethod(); + const twirpClient = internalCacheTwirpClient(); let cacheId = -1; - const cachePaths = yield utils.resolvePaths(paths); - core.debug('Cache Paths:'); - core.debug(`${JSON.stringify(cachePaths)}`); + const cachePaths = yield resolvePaths(paths); + core/* debug */.Yz('Cache Paths:'); + core/* debug */.Yz(`${JSON.stringify(cachePaths)}`); if (cachePaths.length === 0) { throw new Error(`Path Validation Error: Path(s) specified in the action for caching do(es) not exist, hence no cache is being saved.`); } - const archiveFolder = yield utils.createTempDirectory(); - const archivePath = path.join(archiveFolder, utils.getCacheFileName(compressionMethod)); - core.debug(`Archive Path: ${archivePath}`); + const archiveFolder = yield createTempDirectory(); + const archivePath = external_path_.join(archiveFolder, getCacheFileName(compressionMethod)); + core/* debug */.Yz(`Archive Path: ${archivePath}`); try { yield createTar(archiveFolder, cachePaths, compressionMethod); - if (core.isDebug()) { + if (core/* isDebug */._o()) { yield listTar(archivePath, compressionMethod); } - const archiveFileSize = utils.getArchiveFileSizeInBytes(archivePath); - core.debug(`File Size: ${archiveFileSize}`); + const archiveFileSize = getArchiveFileSizeInBytes(archivePath); + core/* debug */.Yz(`File Size: ${archiveFileSize}`); // Set the archive size in the options, will be used to display the upload progress options.archiveSizeBytes = archiveFileSize; - core.debug('Reserving Cache'); - const version = utils.getCacheVersion(paths, compressionMethod, enableCrossOsArchive); + core/* debug */.Yz('Reserving Cache'); + const version = getCacheVersion(paths, compressionMethod, enableCrossOsArchive); const request = { key, version @@ -51962,29 +51962,29 @@ function saveCacheV2(paths_1, key_1, options_1) { // customer-facing warning. if (response.message && !response.message.startsWith(CACHE_WRITE_DENIED_PREFIX)) { - core.warning(`Cache reservation failed: ${response.message}`); + core/* warning */.$e(`Cache reservation failed: ${response.message}`); } throw new Error(response.message || 'Response was not ok'); } signedUploadUrl = response.signedUploadUrl; } catch (error) { - core.debug(`Failed to reserve cache: ${error}`); + core/* debug */.Yz(`Failed to reserve cache: ${error}`); const errorMessage = (_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : ''; if (errorMessage.startsWith(CACHE_WRITE_DENIED_PREFIX)) { throw new CacheWriteDeniedError(`Unable to reserve cache with key ${key}. More details: ${errorMessage}`); } throw new ReserveCacheError(`Unable to reserve cache with key ${key}, another job may be creating this cache.`); } - core.debug(`Attempting to upload cache located at: ${archivePath}`); - yield cacheHttpClient.saveCache(cacheId, archivePath, signedUploadUrl, options); + core/* debug */.Yz(`Attempting to upload cache located at: ${archivePath}`); + yield saveCache(cacheId, archivePath, signedUploadUrl, options); const finalizeRequest = { key, version, sizeBytes: `${archiveFileSize}` }; const finalizeResponse = yield twirpClient.FinalizeCacheEntryUpload(finalizeRequest); - core.debug(`FinalizeCacheEntryUploadResponse: ${finalizeResponse.ok}`); + core/* debug */.Yz(`FinalizeCacheEntryUploadResponse: ${finalizeResponse.ok}`); if (!finalizeResponse.ok) { if (finalizeResponse.message) { throw new FinalizeCacheError(finalizeResponse.message); @@ -51999,33 +51999,33 @@ function saveCacheV2(paths_1, key_1, options_1) { throw error; } else if (typedError.name === ReserveCacheError.name) { - core.info(`Failed to save: ${typedError.message}`); + core/* info */.pq(`Failed to save: ${typedError.message}`); } else if (typedError.name === FinalizeCacheError.name) { - core.warning(typedError.message); + core/* warning */.$e(typedError.message); } else { // Log server errors (5xx) as errors, all other errors as warnings. // A write denied by policy (CacheWriteDeniedError) is not an // HttpClientError and its name does not match the ReserveCacheError arm, // so it falls here and is warned without failing the run. - if (typedError instanceof HttpClientError && + if (typedError instanceof lib/* HttpClientError */.Kg && typeof typedError.statusCode === 'number' && typedError.statusCode >= 500) { - core.error(`Failed to save: ${typedError.message}`); + core/* error */.z3(`Failed to save: ${typedError.message}`); } else { - core.warning(`Failed to save: ${typedError.message}`); + core/* warning */.$e(`Failed to save: ${typedError.message}`); } } } finally { // Try to delete the archive to save space try { - yield utils.unlinkFile(archivePath); + yield unlinkFile(archivePath); } catch (error) { - core.debug(`Failed to delete archive: ${error}`); + core/* debug */.Yz(`Failed to delete archive: ${error}`); } } return cacheId; @@ -52041,11 +52041,10 @@ function saveCacheV2(paths_1, key_1, options_1) { // EXPORTS __webpack_require__.d(__webpack_exports__, { + v: () => (/* binding */ create), y: () => (/* binding */ glob_hashFiles) }); -// UNUSED EXPORTS: create - // EXTERNAL MODULE: ./node_modules/@actions/core/lib/core.js + 7 modules var core = __webpack_require__(3838); // EXTERNAL MODULE: external "fs" diff --git a/dist/setup/index.js b/dist/setup/index.js index 9fe143f0..7854db9d 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -30770,6 +30770,7 @@ module.exports = { /* harmony export */ __nccwpck_require__.d(__webpack_exports__, { /* harmony export */ At: () => (/* binding */ INPUT_CACHE_DEPENDENCY_PATH), /* harmony export */ E8: () => (/* binding */ INPUT_SET_DEFAULT), +/* harmony export */ GL: () => (/* binding */ INPUT_CACHE_JDK), /* harmony export */ I9: () => (/* binding */ INPUT_FORCE_DOWNLOAD), /* harmony export */ K$: () => (/* binding */ GPG_PASSPHRASE_PROFILE_ID), /* harmony export */ LS: () => (/* binding */ INPUT_ARCHITECTURE), @@ -30849,6 +30850,7 @@ const MAVEN_GPG_PASSPHRASE_DEFAULT_ENV = 'MAVEN_GPG_PASSPHRASE'; // Id of the settings.xml profile used to set `gpg.passphraseEnvName`. const GPG_PASSPHRASE_PROFILE_ID = 'setup-java-gpg'; const INPUT_CACHE = 'cache'; +const INPUT_CACHE_JDK = 'cache-jdk'; const INPUT_CACHE_DEPENDENCY_PATH = 'cache-dependency-path'; const INPUT_CACHE_PATH = 'cache-path'; const INPUT_CACHE_READ_ONLY = 'cache-read-only'; @@ -31243,6 +31245,7 @@ function validateToolchainIds(versions, versionFile, toolchainIds) { /* harmony export */ ZY: () => (/* binding */ convertVersionToSemver), /* harmony export */ aT: () => (/* binding */ isGhes), /* harmony export */ ag: () => (/* binding */ getDownloadArchiveExtension), +/* harmony export */ lN: () => (/* binding */ isJdkCacheEnabled), /* harmony export */ n2: () => (/* binding */ renameWinArchive), /* harmony export */ rC: () => (/* binding */ getNextPageUrlFromLinkHeader), /* harmony export */ ri: () => (/* binding */ getLatestMajorVersion), @@ -31286,6 +31289,11 @@ function getBooleanInput(inputName, defaultValue = false) { } throw new Error(`Invalid value '${inputValue}' for boolean input '${inputName}'. Expected 'true' or 'false'.`); } +function isJdkCacheEnabled(cache) { + return _actions_core__WEBPACK_IMPORTED_MODULE_4__/* .getInput */ .V4(_constants_js__WEBPACK_IMPORTED_MODULE_6__/* .INPUT_CACHE_JDK */ .GL).trim() + ? getBooleanInput(_constants_js__WEBPACK_IMPORTED_MODULE_6__/* .INPUT_CACHE_JDK */ .GL) + : Boolean(cache.trim()); +} function getVersionFromToolcachePath(toolPath) { if (toolPath) { return path.basename(path.dirname(toolPath)); @@ -31890,6 +31898,7 @@ __nccwpck_require__.d(__webpack_exports__, { dN: () => (/* binding */ exportVariable), V4: () => (/* binding */ getInput), q3: () => (/* binding */ getMultilineInput), + Gu: () => (/* binding */ getState), pq: () => (/* binding */ info), _o: () => (/* binding */ isDebug), LZ: () => (/* binding */ saveState), @@ -31900,7 +31909,7 @@ __nccwpck_require__.d(__webpack_exports__, { $e: () => (/* binding */ warning) }); -// UNUSED EXPORTS: ExitCode, getBooleanInput, getIDToken, getState, group, markdownSummary, notice, platform, setCommandEcho, summary, toPlatformPath, toPosixPath, toWin32Path +// UNUSED EXPORTS: ExitCode, getBooleanInput, getIDToken, group, markdownSummary, notice, platform, setCommandEcho, summary, toPlatformPath, toPosixPath, toWin32Path // EXTERNAL MODULE: external "os" var external_os_ = __nccwpck_require__(857); @@ -36119,6 +36128,7 @@ async function run() { const packageType = setup_java_core/* getInput */.V4(constants/* INPUT_JAVA_PACKAGE */.p1); const jdkFile = getJdkFileInput(); const cache = setup_java_core/* getInput */.V4(constants/* INPUT_CACHE */.gk); + const cacheJdk = (0,util/* isJdkCacheEnabled */.lN)(cache); const cacheDependencyPath = setup_java_core/* getInput */.V4(constants/* INPUT_CACHE_DEPENDENCY_PATH */.At); const cachePath = setup_java_core/* getMultilineInput */.q3(constants/* INPUT_CACHE_PATH */.uW); const checkLatest = (0,util/* getBooleanInput */.Vt)(constants/* INPUT_CHECK_LATEST */.YM, false); @@ -36157,6 +36167,7 @@ async function run() { packageType, checkLatest, forceDownload, + cacheJdk, setDefault, verifySignature, verifySignaturePublicKey, @@ -36179,6 +36190,7 @@ async function run() { packageType, checkLatest, forceDownload, + cacheJdk, setDefault, verifySignature, verifySignaturePublicKey, @@ -36231,12 +36243,13 @@ function getJdkFileInput() { return jdkFile || deprecatedJdkFile; } async function installVersion(version, options, toolchainId = 0) { - const { distributionName, jdkFile, architecture, packageType, checkLatest, forceDownload, setDefault, verifySignature, verifySignaturePublicKey, toolchainIds } = options; + const { distributionName, jdkFile, architecture, packageType, checkLatest, forceDownload, cacheJdk, setDefault, verifySignature, verifySignaturePublicKey, toolchainIds } = options; const installerOptions = { architecture, packageType, checkLatest, forceDownload, + cacheJdk, setDefault, verifySignature, verifySignaturePublicKey, diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 138ca3a4..2e4f42be 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -17,6 +17,7 @@ - [Package compatibility](#Package-compatibility) - [JavaFX Maven project](#JavaFX-Maven-project) - [Ensuring the Maven cache is complete (plugin dependencies)](#ensuring-the-maven-cache-is-complete-plugin-dependencies) +- [Caching JDK installations](#caching-jdk-installations) - [Installing custom Java architecture](#Installing-custom-Java-architecture) - [Installing JDK without setting as default](#Installing-JDK-without-setting-as-default) - [Installing custom Java distribution from local file](#Installing-Java-from-local-file) @@ -468,6 +469,93 @@ jobs: > which provides purpose-built caching (see the > [setup-gradle documentation](https://github.com/gradle/actions/blob/main/docs/setup-gradle.md)). +## Caching JDK installations + +`cache-jdk` controls caching for downloaded JDK installations. The JDK cache is +stored and restored as its own cache entry, separate from the dependency and +build-tool wrapper caches selected by `cache`. Whether it is *enabled*, however, +is coupled to `cache`: setting `cache` turns JDK caching on as well, unless +`cache-jdk` is set explicitly. + +| `cache` | `cache-jdk` | Dependency and wrapper caches | JDK cache | +| --- | --- | --- | --- | +| Omitted | Omitted | Disabled | Disabled | +| Omitted | `true` | Disabled | Enabled | +| Omitted | `false` | Disabled | Disabled | +| Set | Omitted | Enabled | Enabled | +| Set | `true` | Enabled | Enabled | +| Set | `false` | Enabled | Disabled | + +JDK entries are specific to the runner operating system and normalized +architecture. They are additionally separated by distribution, package type, +exact resolved Java version, release identity, and signature-verification +identity. The release identity is the authoritative checksum when available and +otherwise the download URL without its query string. These dimensions prevent +incompatible JDKs from sharing an entry. They also mean that a matrix or workflow +using multiple JDK versions, distributions, package types, architectures, or +operating systems stores a separate JDK entry for each identity and consumes +cache storage for each one. + +For `distribution: jdkfile`, the release source is a SHA-256 hash of the local +`jdk-file` contents, streamed so the archive is not held in memory. Changing the +archive therefore creates a different JDK cache entry, even when its path and +requested version are unchanged. The archive is only read when the runner tool +cache holds no installation satisfying the requested version: a matching +tool-cache installation short-circuits setup, so a changed `jdk-file` is not +re-extracted for a version that is already installed. Use +`force-download: true` when the archive contents change but the version does not. + +The verification identity separates unverified downloads from packages verified +with the distribution's bundled signing key and from packages verified with each +custom key. Custom public keys are represented by a SHA-256 fingerprint of +normalized key material; the key itself is not placed in the cache key, the logs, +or action state. A verified exact-key hit reuses content that was +signature-verified when it was downloaded by the run that saved the entry, +instead of downloading and verifying it again. + +> [!IMPORTANT] +> The JDK cache **key** is what isolates verification modes and release +> identity: a JDK cache entry created by an unverified download can never be +> restored for a request that sets `verify-signature: true`, and vice versa. +> `cache-jdk` does not change how the runner tool cache is used. setup-java +> first looks for an installation in the runner tool cache — a preinstalled +> JDK, or one installed by an earlier step of the same job — and uses it as-is. Such an installation is not downloaded again, and its checksum +> and signature are not reverified, even when `verify-signature: true` is set, +> because its verification history is not recorded in the tool cache. Use +> `force-download: true` for a request that must download and verify the archive +> itself. + +`check-latest: true` and `java-version: latest` resolve remote metadata before +looking up the exact resolved JDK entry. `force-download: true` bypasses both the +runner tool cache and JDK cache restore, but an enabled JDK cache still records +the downloaded installation for a post-job save. `cache-read-only: true` allows +restores but suppresses post-job saves for JDK, dependency, and wrapper caches. + +If the cache service fails to restore an entry, or the restored entry lacks the +expected completed tool-cache path, setup continues by downloading the JDK. +Post-job saves are best-effort and do not fail the job: cache keys are immutable, +so an existing key or a concurrent job winning the save race is left unchanged, +and a failure to save one JDK entry is reported as a warning without preventing +the remaining entries from being saved. + +A key is only ever populated with the installation it was computed for. Because +tool-cache paths are shared per version and architecture, a later step — for +example one using `force-download: true` — can replace the installation an +earlier step registered. setup-java detects that replacement in the post-job +step and skips the save with a warning, so a key is never saved with content +other than the installation it identifies. This guarantee holds without +rehashing hundreds of megabytes of JDK content on every job. + +JDK caching trades cache storage and cold-run save work for faster warm setup. +In a five-run Ubuntu benchmark using Microsoft OpenJDK 17.0.19, the median warm +`setup-java` time fell from 7 seconds to 3 seconds and median warm job time fell +from 24 seconds to 18 seconds. The JDK entry added 175.3 MiB for that single +identity. Results vary by runner, distribution, JDK size, network, and cache +eviction pressure; short jobs may improve latency without changing billed +minutes. The benchmark harness and methodology, along with results from later +runs, live in +[actions/setup-java-benchmarks](https://github.com/actions/setup-java-benchmarks). + ## Platform and architecture compatibility The `architecture` input is normalized before setup-java checks the tool cache diff --git a/package.json b/package.json index e1474c23..73ea57c8 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "fix": "npm run format && npm run lint:fix && npm run build", "prepare": "husky install", "prerelease": "npm run-script build", - "release": "git add -f dist/setup/*.js dist/setup/package.json dist/cleanup/index.js", + "release": "git add -f dist/setup/*.js dist/setup/package.json dist/cleanup/*.js", "test": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --runInBand --coverage" }, "lint-staged": { diff --git a/src/cleanup-java.ts b/src/cleanup-java.ts index 3716098e..b1962e63 100644 --- a/src/cleanup-java.ts +++ b/src/cleanup-java.ts @@ -1,7 +1,11 @@ import * as core from '@actions/core'; import * as gpg from './gpg.js'; import * as constants from './constants.js'; -import {getBooleanInput, isJobStatusSuccess} from './util.js'; +import { + getBooleanInput, + isJdkCacheEnabled, + isJobStatusSuccess +} from './util.js'; import {fileURLToPath} from 'url'; async function removePrivateKeyFromKeychain() { @@ -24,10 +28,11 @@ async function removePrivateKeyFromKeychain() { * Check given input and run a save process for the specified package manager * @returns Promise that will be resolved when the save process finishes */ -async function saveCache() { +async function saveCaches() { const jobStatus = isJobStatusSuccess(); const cache = core.getInput(constants.INPUT_CACHE); - if (!jobStatus || !cache) { + const cacheJdk = isJdkCacheEnabled(cache); + if (!jobStatus || (!cache && !cacheJdk)) { return; } @@ -36,8 +41,16 @@ async function saveCache() { return; } - const {save} = await import('./cache.js'); - await save(cache); + const saves: Promise[] = []; + if (cache) { + const {save} = await import('./cache.js'); + saves.push(save(cache)); + } + if (cacheJdk) { + const {saveJdkCaches} = await import('./jdk-cache.js'); + saves.push(saveJdkCaches()); + } + await Promise.all(saves); } /** @@ -59,7 +72,7 @@ async function ignoreError(promise: Promise) { export async function run() { await removePrivateKeyFromKeychain(); - await ignoreError(saveCache()); + await ignoreError(saveCaches()); } if (process.argv[1] === fileURLToPath(import.meta.url)) { diff --git a/src/constants.ts b/src/constants.ts index e95bae18..6e19a587 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -37,6 +37,7 @@ export const MAVEN_GPG_PASSPHRASE_DEFAULT_ENV = 'MAVEN_GPG_PASSPHRASE'; export const GPG_PASSPHRASE_PROFILE_ID = 'setup-java-gpg'; export const INPUT_CACHE = 'cache'; +export const INPUT_CACHE_JDK = 'cache-jdk'; 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'; diff --git a/src/distributions/base-installer.ts b/src/distributions/base-installer.ts index d771b927..34d6c7cf 100644 --- a/src/distributions/base-installer.ts +++ b/src/distributions/base-installer.ts @@ -21,6 +21,7 @@ import {RetryingHttpClient} from '../retrying-http-client.js'; import os from 'os'; import {expectedDigestLength, verifyChecksum} from '../checksum.js'; import {normalizeArchitecture} from './platform-types.js'; +import type {JdkCache} from '../jdk-cache.js'; export abstract class JavaBase { protected http: httpm.HttpClient; @@ -31,6 +32,7 @@ export abstract class JavaBase { protected latest: boolean; protected checkLatest: boolean; protected forceDownload: boolean; + protected cacheJdk: boolean; protected setDefault: boolean; protected verifySignature: boolean; protected verifySignaturePublicKey: string | undefined; @@ -52,6 +54,7 @@ export abstract class JavaBase { this.packageType = installerOptions.packageType; this.checkLatest = installerOptions.checkLatest; this.forceDownload = installerOptions.forceDownload ?? false; + this.cacheJdk = installerOptions.cacheJdk ?? false; this.setDefault = installerOptions.setDefault !== undefined ? installerOptions.setDefault @@ -180,9 +183,48 @@ export abstract class JavaBase { if (!this.forceDownload && foundJava?.version === javaRelease.version) { core.info(`Resolved Java ${foundJava.version} from tool-cache`); } else { - core.info('Trying to download...'); - foundJava = await this.downloadTool(javaRelease); - core.info(`Java ${foundJava.version} was downloaded`); + let jdkCache: JdkCache | undefined; + if (this.cacheJdk) { + const {getJdkVerificationIdentity} = + await import('../jdk-cache.js'); + jdkCache = { + distribution: this.distribution, + packageType: this.packageType, + architecture: this.architecture, + version: javaRelease.version, + source: this.getJdkReleaseIdentity(javaRelease), + verification: getJdkVerificationIdentity( + this.verifySignature, + this.verifySignaturePublicKey + ), + path: this.getJdkCachePath(javaRelease.version) + }; + } + if (!this.forceDownload && jdkCache) { + const {restoreJdk} = await import('../jdk-cache.js'); + const restored = await restoreJdk(jdkCache); + if (restored) { + const restoredPath = this.getRestoredJdkPath(javaRelease.version); + if (restoredPath) { + foundJava = { + version: javaRelease.version, + path: restoredPath + }; + } + } + } + if (!foundJava || foundJava.version !== javaRelease.version) { + core.info('Trying to download...'); + foundJava = await this.downloadTool(javaRelease); + core.info(`Java ${foundJava.version} was downloaded`); + if (jdkCache) { + // Register after the installation exists so its identity is + // captured; the post-job save refuses to upload a path whose + // installation was replaced afterwards. + const {registerJdk} = await import('../jdk-cache.js'); + registerJdk(jdkCache); + } + } } } catch (error: any) { this.logSetupError(error); @@ -299,6 +341,42 @@ export abstract class JavaBase { return version.replace('+', '-'); } + protected getJdkCachePath(version: string): string { + const toolCache = process.env['RUNNER_TOOL_CACHE']; + if (!toolCache) { + return ''; + } + return path.join( + toolCache, + this.toolcacheFolderName, + this.getToolcacheVersionName(version) + ); + } + + protected getRestoredJdkPath(version: string): string | null { + const basePath = this.getJdkCachePath(version); + if (!basePath) { + return null; + } + const architecturePath = path.join(basePath, this.architecture); + return fs.existsSync(architecturePath) && + fs.existsSync(`${architecturePath}.complete`) + ? architecturePath + : null; + } + + private getJdkReleaseIdentity(javaRelease: JavaDownloadRelease): string { + if (javaRelease.checksum) { + return `${javaRelease.checksum.algorithm}:${javaRelease.checksum.value}`; + } + try { + const url = new URL(javaRelease.url); + return `${url.origin}${url.pathname}`; + } catch { + return javaRelease.url; + } + } + protected findInToolcache(): JavaInstallerResults | null { // we can't use tc.find directly because firstly, we need to filter versions by stability flag // if *-ea is provided, take only ea versions from toolcache, otherwise - only stable versions diff --git a/src/distributions/base-models.ts b/src/distributions/base-models.ts index 2bec34f4..44706fda 100644 --- a/src/distributions/base-models.ts +++ b/src/distributions/base-models.ts @@ -4,6 +4,7 @@ export interface JavaInstallerOptions { packageType: string; checkLatest: boolean; forceDownload?: boolean; + cacheJdk?: boolean; setDefault?: boolean; verifySignature?: boolean; verifySignaturePublicKey?: string; diff --git a/src/distributions/local/installer.ts b/src/distributions/local/installer.ts index 840c19dc..f9c492c6 100644 --- a/src/distributions/local/installer.ts +++ b/src/distributions/local/installer.ts @@ -12,6 +12,9 @@ import { } from '../base-models.js'; import {extractJdkFile} from '../../util.js'; import {MACOS_JAVA_CONTENT_POSTFIX} from '../../constants.js'; +import {createReadStream} from 'fs'; +import {createHash} from 'crypto'; +import type {JdkCache} from '../../jdk-cache.js'; export class LocalDistribution extends JavaBase { constructor( @@ -27,6 +30,11 @@ export class LocalDistribution extends JavaBase { "The 'latest' version alias is not supported for the 'jdkfile' distribution. Please specify a concrete version." ); } + if (this.verifySignature) { + throw new Error( + `Input 'verify-signature' is not supported for distribution '${this.distribution}'.` + ); + } let foundJava = this.forceDownload ? null : this.findInToolcache(); @@ -46,24 +54,60 @@ export class LocalDistribution extends JavaBase { throw new Error(`JDK file was not found in path '${jdkFilePath}'`); } - core.info(`Extracting Java from '${jdkFilePath}'`); + let jdkCache: JdkCache | undefined; + if (this.cacheJdk) { + const [{getJdkVerificationIdentity}, source] = await Promise.all([ + import('../../jdk-cache.js'), + hashFile(jdkFilePath) + ]); + jdkCache = { + distribution: this.distribution, + packageType: this.packageType, + architecture: this.architecture, + version: this.version, + source, + verification: getJdkVerificationIdentity(false), + path: this.getJdkCachePath(this.version) + }; + } + if (!this.forceDownload && jdkCache) { + const {restoreJdk} = await import('../../jdk-cache.js'); + const restored = await restoreJdk(jdkCache); + const restoredPath = restored + ? this.getRestoredJdkPath(this.version) + : undefined; + if (restoredPath) { + foundJava = { + version: this.version, + path: restoredPath + }; + } + } - const extractedJavaPath = await extractJdkFile(jdkFilePath); - const archiveName = fs.readdirSync(extractedJavaPath)[0]; - const archivePath = path.join(extractedJavaPath, archiveName); - const javaVersion = this.version; + if (!foundJava) { + core.info(`Extracting Java from '${jdkFilePath}'`); - const javaPath = await tc.cacheDir( - archivePath, - this.toolcacheFolderName, - this.getToolcacheVersionName(javaVersion), - this.architecture - ); + const extractedJavaPath = await extractJdkFile(jdkFilePath); + const archiveName = fs.readdirSync(extractedJavaPath)[0]; + const archivePath = path.join(extractedJavaPath, archiveName); + const javaVersion = this.version; - foundJava = { - version: javaVersion, - path: javaPath - }; + const javaPath = await tc.cacheDir( + archivePath, + this.toolcacheFolderName, + this.getToolcacheVersionName(javaVersion), + this.architecture + ); + + foundJava = { + version: javaVersion, + path: javaPath + }; + if (jdkCache) { + const {registerJdk} = await import('../../jdk-cache.js'); + registerJdk(jdkCache); + } + } } // JDK folder may contain postfix "Contents/Home" on macOS @@ -103,3 +147,11 @@ export class LocalDistribution extends JavaBase { ); } } + +async function hashFile(file: string): Promise { + const hash = createHash('sha256'); + for await (const chunk of createReadStream(file)) { + hash.update(chunk); + } + return hash.digest('hex'); +} diff --git a/src/jdk-cache.ts b/src/jdk-cache.ts new file mode 100644 index 00000000..afee9206 --- /dev/null +++ b/src/jdk-cache.ts @@ -0,0 +1,239 @@ +import {createHash} from 'crypto'; +import fs from 'fs'; +import path from 'path'; +import * as cache from '@actions/cache'; +import * as core from '@actions/core'; +import {isCacheFeatureAvailable} from './cache-feature.js'; + +const STATE_JDK_CACHES = 'jdk-caches'; +const JDK_CACHE_KEY_VERSION = 1; + +export interface JdkCache { + distribution: string; + packageType: string; + architecture: string; + version: string; + source: string; + verification: string; + path: string; +} + +interface JdkCacheState { + key: string; + path: string; + architecture: string; + matchedKey?: string; + // Cheap identity of the installation that occupied `path` when the entry was + // registered. The tool-cache path is shared per version/architecture, so a + // later step (e.g. one using `force-download`) can replace those bytes; the + // post-job save must not upload content that does not match the identity the + // key was computed for. + installation?: string; +} + +const restoredCaches: JdkCacheState[] = []; + +export async function restoreJdk(jdk: JdkCache): Promise { + if (!jdk.path || !isCacheFeatureAvailable()) { + return false; + } + + const key = buildJdkCacheKey(jdk); + let matchedKey: string | undefined; + try { + matchedKey = await cache.restoreCache([jdk.path], key); + } catch (error) { + core.warning(`Failed to restore JDK cache: ${(error as Error).message}`); + } + + const architecturePath = path.join(jdk.path, jdk.architecture); + if ( + matchedKey && + (!fs.existsSync(architecturePath) || + !fs.existsSync(`${architecturePath}.complete`)) + ) { + core.warning( + `JDK cache key ${matchedKey} was restored without the expected tool-cache path; downloading the JDK instead.` + ); + matchedKey = undefined; + } + + recordJdkCache({ + key, + path: jdk.path, + architecture: jdk.architecture, + matchedKey + }); + + if (matchedKey) { + core.info(`JDK cache restored from key: ${matchedKey}`); + return true; + } + + core.info(`JDK cache is not found for ${jdk.distribution} ${jdk.version}`); + return false; +} + +export function registerJdk(jdk: JdkCache): void { + if (!jdk.path) { + return; + } + recordJdkCache({ + key: buildJdkCacheKey(jdk), + path: jdk.path, + architecture: jdk.architecture, + installation: getInstallationIdentity(jdk.path, jdk.architecture) + }); +} + +/** + * Cheap fingerprint of the installation stored at a tool-cache path. The + * `.complete` marker is (re)created by `tc.cacheDir` every time an + * installation is written, so its inode and timestamps change whenever the + * installation is replaced. This avoids rehashing a multi-hundred-megabyte JDK + * directory while still detecting that the bytes behind a key were swapped. + */ +function getInstallationIdentity( + jdkPath: string, + architecture: string +): string | undefined { + const architecturePath = path.join(jdkPath, architecture); + try { + const marker = fs.statSync(`${architecturePath}.complete`); + const installation = fs.statSync(architecturePath); + return [ + marker.ino, + marker.mtimeMs, + marker.ctimeMs, + marker.size, + installation.ino, + installation.mtimeMs, + installation.ctimeMs + ].join(':'); + } catch { + return undefined; + } +} + +export function getJdkVerificationIdentity( + verifySignature: boolean, + publicKey?: string +): string { + if (!verifySignature) { + return 'unverified'; + } + if (!publicKey) { + return 'verified:bundled'; + } + + const normalizedKey = publicKey.replace(/\r\n?/g, '\n').trim(); + const fingerprint = createHash('sha256').update(normalizedKey).digest('hex'); + return `verified:custom:sha256:${fingerprint}`; +} + +export async function saveJdkCaches(): Promise { + const state = core.getState(STATE_JDK_CACHES); + if (!state) { + return; + } + + const caches = parseJdkCacheState(state); + for (const jdk of caches) { + if (jdk.matchedKey === jdk.key) { + core.info( + `Cache hit occurred on the JDK primary key ${jdk.key}, not saving cache.` + ); + continue; + } + + if (!fs.existsSync(jdk.path)) { + core.debug(`JDK cache path does not exist, not saving: ${jdk.path}`); + continue; + } + + if (!jdk.installation) { + core.debug( + `No JDK installation was registered for the key ${jdk.key}, not saving cache.` + ); + continue; + } + + if ( + getInstallationIdentity(jdk.path, jdk.architecture) !== jdk.installation + ) { + core.warning( + `The JDK installation in ${jdk.path} was replaced after it was registered for the key ${jdk.key}; not saving cache.` + ); + continue; + } + + try { + const cacheId = await cache.saveCache([jdk.path], jdk.key); + if (cacheId !== -1) { + core.info(`JDK cache saved with the key: ${jdk.key}`); + } + } catch (error) { + const err = error as Error; + if (err.name === cache.ReserveCacheError.name) { + core.info(err.message); + } else { + // Saving is best-effort and per entry: one failure must not suppress + // the remaining JDK caches. + core.warning( + `Failed to save the JDK cache with the key ${jdk.key}: ${err.message}` + ); + } + } + } +} + +export function buildJdkCacheKey(jdk: JdkCache): string { + const runnerOs = process.env['RUNNER_OS'] ?? process.platform; + const normalizedArchitecture = jdk.architecture.toLowerCase(); + const identity = JSON.stringify({ + keyVersion: JDK_CACHE_KEY_VERSION, + runnerOs, + distribution: jdk.distribution.toLowerCase(), + packageType: jdk.packageType.toLowerCase(), + architecture: normalizedArchitecture, + version: jdk.version, + source: jdk.source, + verification: jdk.verification + }); + const digest = createHash('sha256').update(identity).digest('hex'); + return `setup-java-jdk-v${JDK_CACHE_KEY_VERSION}-${runnerOs}-${normalizedArchitecture}-${digest}`; +} + +function recordJdkCache(jdk: JdkCacheState): void { + const existing = restoredCaches.findIndex( + item => item.key === jdk.key && item.path === jdk.path + ); + if (existing === -1) { + restoredCaches.push(jdk); + } else { + restoredCaches[existing] = {...restoredCaches[existing], ...jdk}; + } + core.saveState(STATE_JDK_CACHES, JSON.stringify(restoredCaches)); +} + +function parseJdkCacheState(state: string): JdkCacheState[] { + const value: unknown = JSON.parse(state); + if ( + !Array.isArray(value) || + !value.every( + item => + typeof item === 'object' && + item !== null && + typeof (item as JdkCacheState).key === 'string' && + typeof (item as JdkCacheState).path === 'string' && + typeof (item as JdkCacheState).architecture === 'string' && + ((item as JdkCacheState).matchedKey === undefined || + typeof (item as JdkCacheState).matchedKey === 'string') && + ((item as JdkCacheState).installation === undefined || + typeof (item as JdkCacheState).installation === 'string') + ) + ) { + throw new Error('Invalid JDK cache information retrieved from state.'); + } + return value as JdkCacheState[]; +} diff --git a/src/setup-java.ts b/src/setup-java.ts index e998f7b6..c361c5ff 100644 --- a/src/setup-java.ts +++ b/src/setup-java.ts @@ -1,6 +1,10 @@ import fs from 'fs'; import * as core from '@actions/core'; -import {getBooleanInput, getVersionFromFileContent} from './util.js'; +import { + getBooleanInput, + getVersionFromFileContent, + isJdkCacheEnabled +} from './util.js'; import * as constants from './constants.js'; import * as path from 'path'; import {fileURLToPath} from 'url'; @@ -17,6 +21,7 @@ export async function run() { const packageType = core.getInput(constants.INPUT_JAVA_PACKAGE); const jdkFile = getJdkFileInput(); const cache = core.getInput(constants.INPUT_CACHE); + const cacheJdk = isJdkCacheEnabled(cache); const cacheDependencyPath = core.getInput( constants.INPUT_CACHE_DEPENDENCY_PATH ); @@ -80,6 +85,7 @@ export async function run() { packageType, checkLatest, forceDownload, + cacheJdk, setDefault, verifySignature, verifySignaturePublicKey, @@ -105,6 +111,7 @@ export async function run() { packageType, checkLatest, forceDownload, + cacheJdk, setDefault, verifySignature, verifySignaturePublicKey, @@ -183,6 +190,7 @@ async function installVersion( packageType, checkLatest, forceDownload, + cacheJdk, setDefault, verifySignature, verifySignaturePublicKey, @@ -194,6 +202,7 @@ async function installVersion( packageType, checkLatest, forceDownload, + cacheJdk, setDefault, verifySignature, verifySignaturePublicKey, @@ -238,6 +247,7 @@ interface installerInputsOptions { packageType: string; checkLatest: boolean; forceDownload: boolean; + cacheJdk: boolean; setDefault: boolean; verifySignature: boolean; verifySignaturePublicKey: string | undefined; diff --git a/src/util.ts b/src/util.ts index 3706ab9d..6bcd0d17 100644 --- a/src/util.ts +++ b/src/util.ts @@ -8,7 +8,8 @@ import * as tc from '@actions/tool-cache'; import * as httpm from '@actions/http-client'; import { INPUT_JOB_STATUS, - DISTRIBUTIONS_ONLY_MAJOR_VERSION + DISTRIBUTIONS_ONLY_MAJOR_VERSION, + INPUT_CACHE_JDK } from './constants.js'; import {OutgoingHttpHeaders} from 'http'; @@ -37,6 +38,12 @@ export function getBooleanInput(inputName: string, defaultValue = false) { ); } +export function isJdkCacheEnabled(cache: string): boolean { + return core.getInput(INPUT_CACHE_JDK).trim() + ? getBooleanInput(INPUT_CACHE_JDK) + : Boolean(cache.trim()); +} + export function getVersionFromToolcachePath(toolPath: string) { if (toolPath) { return path.basename(path.dirname(toolPath));