diff --git a/README.md b/README.md index 9ec3545e5..eb80558fb 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 8a4c04585..82f7b3671 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 f8aa0d241..b41ccf4c1 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 47997888e..f20ff6ad4 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 000000000..63788a78d --- /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 576289270..7705abfc4 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 5538ed7d2..a693d3f45 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 004810870..05bf6deb1 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 280f83c1e..6146f2c94 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 000000000..1d80a1366 --- /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 6624703b0..5770ae795 100644 --- a/dist/cleanup/index.js +++ b/dist/cleanup/index.js @@ -30764,1536 +30764,1937 @@ module.exports = { /***/ }), -/***/ 2613: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("assert"); - -/***/ }), - -/***/ 181: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("buffer"); - -/***/ }), - -/***/ 5317: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("child_process"); - -/***/ }), - -/***/ 6982: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("crypto"); - -/***/ }), - -/***/ 4434: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("events"); - -/***/ }), - -/***/ 9896: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("fs"); - -/***/ }), - -/***/ 8611: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("http"); - -/***/ }), - -/***/ 5692: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("https"); - -/***/ }), - -/***/ 9278: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("net"); - -/***/ }), - -/***/ 4589: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:assert"); - -/***/ }), - -/***/ 6698: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:async_hooks"); - -/***/ }), - -/***/ 4573: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:buffer"); - -/***/ }), - -/***/ 7540: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:console"); - -/***/ }), - -/***/ 7598: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:crypto"); - -/***/ }), - -/***/ 3053: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:diagnostics_channel"); - -/***/ }), - -/***/ 610: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:dns"); - -/***/ }), - -/***/ 8474: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:events"); - -/***/ }), - -/***/ 3024: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:fs"); - -/***/ }), - -/***/ 7067: -/***/ ((module) => { +/***/ 7242: +/***/ ((__unused_webpack_module, __webpack_exports__, __nccwpck_require__) => { -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:http"); - -/***/ }), - -/***/ 2467: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:http2"); - -/***/ }), - -/***/ 4708: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:https"); - -/***/ }), - -/***/ 8995: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:module"); - -/***/ }), - -/***/ 7030: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:net"); - -/***/ }), - -/***/ 8161: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:os"); - -/***/ }), - -/***/ 6760: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:path"); - -/***/ }), - -/***/ 643: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:perf_hooks"); - -/***/ }), - -/***/ 1708: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:process"); - -/***/ }), - -/***/ 1792: -/***/ ((module) => { +/* 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'])); -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:querystring"); /***/ }), -/***/ 7075: -/***/ ((module) => { +/***/ 4527: +/***/ ((__unused_webpack_module, __webpack_exports__, __nccwpck_require__) => { -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:stream"); +/* 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); -/***/ }), -/***/ 1692: -/***/ ((module) => { -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:tls"); -/***/ }), -/***/ 3136: -/***/ ((module) => { -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:url"); -/***/ }), - -/***/ 7975: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:util"); - -/***/ }), - -/***/ 3429: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:util/types"); - -/***/ }), - -/***/ 5919: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:worker_threads"); - -/***/ }), - -/***/ 8522: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:zlib"); - -/***/ }), - -/***/ 857: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("os"); - -/***/ }), - -/***/ 6928: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("path"); - -/***/ }), - -/***/ 2203: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("stream"); - -/***/ }), - -/***/ 3193: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("string_decoder"); - -/***/ }), - -/***/ 4756: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("tls"); - -/***/ }), - -/***/ 2018: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("tty"); - -/***/ }), - -/***/ 7016: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("url"); - -/***/ }), - -/***/ 9023: -/***/ ((module) => { - -module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("util"); - -/***/ }), - -/***/ 3838: -/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __nccwpck_require__) => { - - -// EXPORTS -__nccwpck_require__.d(__webpack_exports__, { - Yz: () => (/* binding */ core_debug), - z3: () => (/* binding */ error), - V4: () => (/* binding */ getInput), - Gu: () => (/* binding */ getState), - pq: () => (/* binding */ info), - _o: () => (/* binding */ isDebug), - C1: () => (/* binding */ setFailed), - Pq: () => (/* binding */ core_setSecret), - $e: () => (/* binding */ warning) -}); - -// UNUSED EXPORTS: ExitCode, addPath, endGroup, exportVariable, getBooleanInput, getIDToken, getMultilineInput, group, markdownSummary, notice, platform, saveState, setCommandEcho, setOutput, startGroup, summary, toPlatformPath, toPosixPath, toWin32Path - -// EXTERNAL MODULE: external "os" -var external_os_ = __nccwpck_require__(857); -;// CONCATENATED MODULE: ./node_modules/@actions/core/lib/utils.js -// We use any as a valid input type -/* eslint-disable @typescript-eslint/no-explicit-any */ -/** - * Sanitizes an input into a string so it can be passed into issueCommand safely - * @param input input to sanitize into a string - */ -function utils_toCommandValue(input) { - if (input === null || input === undefined) { - return ''; +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; } - else if (typeof input === 'string' || input instanceof String) { - return input; + if (normalizedValue === 'true') { + return true; } - return JSON.stringify(input); -} -/** - * - * @param annotationProperties - * @returns The command properties to send with the actual annotation command - * See IssueCommandProperties: https://github.com/actions/runner/blob/main/src/Runner.Worker/ActionCommandManager.cs#L646 - */ -function utils_toCommandProperties(annotationProperties) { - if (!Object.keys(annotationProperties).length) { - return {}; + if (normalizedValue === 'false') { + return false; } - return { - title: annotationProperties.title, - file: annotationProperties.file, - line: annotationProperties.startLine, - endLine: annotationProperties.endLine, - col: annotationProperties.startColumn, - endColumn: annotationProperties.endColumn - }; + throw new Error(`Invalid value '${inputValue}' for boolean input '${inputName}'. Expected 'true' or 'false'.`); } -//# sourceMappingURL=utils.js.map -;// CONCATENATED MODULE: ./node_modules/@actions/core/lib/command.js - - -/** - * Issues a command to the GitHub Actions runner - * - * @param command - The command name to issue - * @param properties - Additional properties for the command (key-value pairs) - * @param message - The message to include with the command - * @remarks - * This function outputs a specially formatted string to stdout that the Actions - * runner interprets as a command. These commands can control workflow behavior, - * set outputs, create annotations, mask values, and more. - * - * Command Format: - * ::name key=value,key=value::message - * - * @example - * ```typescript - * // Issue a warning annotation - * issueCommand('warning', {}, 'This is a warning message'); - * // Output: ::warning::This is a warning message - * - * // Set an environment variable - * issueCommand('set-env', { name: 'MY_VAR' }, 'some value'); - * // Output: ::set-env name=MY_VAR::some value - * - * // Add a secret mask - * issueCommand('add-mask', {}, 'secretValue123'); - * // Output: ::add-mask::secretValue123 - * ``` - * - * @internal - * This is an internal utility function that powers the public API functions - * such as setSecret, warning, error, and exportVariable. - */ -function command_issueCommand(command, properties, message) { - const cmd = new Command(command, properties, message); - process.stdout.write(cmd.toString() + external_os_.EOL); +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 command_issue(name, message = '') { - command_issueCommand(name, {}, message); +function getVersionFromToolcachePath(toolPath) { + if (toolPath) { + return path.basename(path.dirname(toolPath)); + } + return toolPath; } -const CMD_STRING = '::'; -class Command { - constructor(command, properties, message) { - if (!command) { - command = 'missing.command'; +async function extractJdkFile(toolPath, extension) { + if (!extension) { + extension = toolPath.endsWith('.tar.gz') + ? 'tar.gz' + : path.extname(toolPath); + if (extension.startsWith('.')) { + extension = extension.substring(1); } - this.command = command; - this.properties = properties; - this.message = message; } - toString() { - let cmdStr = CMD_STRING + this.command; - if (this.properties && Object.keys(this.properties).length > 0) { - cmdStr += ' '; - let first = true; - for (const key in this.properties) { - if (this.properties.hasOwnProperty(key)) { - const val = this.properties[key]; - if (val) { - if (first) { - first = false; - } - else { - cmdStr += ','; - } - cmdStr += `${key}=${escapeProperty(val)}`; - } - } - } - } - cmdStr += `${CMD_STRING}${escapeData(this.message)}`; - return cmdStr; + 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 escapeData(s) { - return utils_toCommandValue(s) - .replace(/%/g, '%25') - .replace(/\r/g, '%0D') - .replace(/\n/g, '%0A'); +function getDownloadArchiveExtension() { + return process.platform === 'win32' ? 'zip' : 'tar.gz'; } -function escapeProperty(s) { - return utils_toCommandValue(s) - .replace(/%/g, '%25') - .replace(/\r/g, '%0D') - .replace(/\n/g, '%0A') - .replace(/:/g, '%3A') - .replace(/,/g, '%2C'); -} -//# sourceMappingURL=command.js.map -// EXTERNAL MODULE: external "crypto" -var external_crypto_ = __nccwpck_require__(6982); -// EXTERNAL MODULE: external "fs" -var external_fs_ = __nccwpck_require__(9896); -;// CONCATENATED MODULE: ./node_modules/@actions/core/lib/file-command.js -// For internal use, subject to change. -// We use any as a valid input type -/* eslint-disable @typescript-eslint/no-explicit-any */ - - - - -function file_command_issueFileCommand(command, message) { - const filePath = process.env[`GITHUB_${command}`]; - if (!filePath) { - throw new Error(`Unable to find environment variable for file command ${command}`); +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 (!fs.existsSync(filePath)) { - throw new Error(`Missing file at path: ${filePath}`); + 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; + } } - fs.appendFileSync(filePath, `${toCommandValue(message)}${os.EOL}`, { - encoding: 'utf8' - }); + return semver.satisfies(version, range); } -function file_command_prepareKeyValueMessage(key, value) { - const delimiter = `ghadelimiter_${crypto.randomUUID()}`; - const convertedValue = toCommandValue(value); - // These should realistically never happen, but just in case someone finds a - // way to exploit uuid generation let's not allow keys or values that contain - // the delimiter. - if (key.includes(delimiter)) { - throw new Error(`Unexpected input: name should not contain the delimiter "${delimiter}"`); - } - if (convertedValue.includes(delimiter)) { - throw new Error(`Unexpected input: value should not contain the delimiter "${delimiter}"`); +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 `${key}<<${delimiter}${os.EOL}${convertedValue}${os.EOL}${delimiter}`; + return null; } -//# sourceMappingURL=file-command.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: ./node_modules/@actions/http-client/lib/auth.js -var auth = __nccwpck_require__(2145); -;// CONCATENATED MODULE: ./node_modules/@actions/core/lib/oidc-utils.js -var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; - - - -class oidc_utils_OidcClient { - static createHttpClient(allowRetry = true, maxRetry = 10) { - const requestOptions = { - allowRetries: allowRetry, - maxRetries: maxRetry - }; - return new HttpClient('actions/oidc-client', [new BearerCredentialHandler(oidc_utils_OidcClient.getRequestToken())], requestOptions); - } - static getRequestToken() { - const token = process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN']; - if (!token) { - throw new Error('Unable to get ACTIONS_ID_TOKEN_REQUEST_TOKEN env variable'); - } - return token; - } - static getIDTokenUrl() { - const runtimeUrl = process.env['ACTIONS_ID_TOKEN_REQUEST_URL']; - if (!runtimeUrl) { - throw new Error('Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable'); - } - return runtimeUrl; +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); } - static getCall(id_token_url) { - return __awaiter(this, void 0, void 0, function* () { - var _a; - const httpclient = oidc_utils_OidcClient.createHttpClient(); - const res = yield httpclient - .getJson(id_token_url) - .catch(error => { - throw new Error(`Failed to get ID Token. \n - Error Code : ${error.statusCode}\n - Error Message: ${error.message}`); - }); - const id_token = (_a = res.result) === null || _a === void 0 ? void 0 : _a.value; - if (!id_token) { - throw new Error('Response json body do not have ID Token field'); - } - return id_token; - }); + 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; } - static getIDToken(audience) { - return __awaiter(this, void 0, void 0, function* () { - try { - // New ID Token is requested from action service - let id_token_url = oidc_utils_OidcClient.getIDTokenUrl(); - if (audience) { - const encodedAudience = encodeURIComponent(audience); - id_token_url = `${id_token_url}&audience=${encodedAudience}`; - } - debug(`ID token url is ${id_token_url}`); - const id_token = yield oidc_utils_OidcClient.getCall(id_token_url); - setSecret(id_token); - return id_token; - } - catch (error) { - throw new Error(`Error message: ${error.message}`); - } - }); + else if (versionFileName == '.sdkmanrc') { + // Match both version and optional distribution identifier + javaVersionRegExp = + /^java\s*=\s*(?[^-\s]+)(?:-(?[a-z0-9]+))?/m; } -} -//# sourceMappingURL=oidc-utils.js.map -;// CONCATENATED MODULE: ./node_modules/@actions/core/lib/summary.js -var summary_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; - - -const { access, appendFile, writeFile } = external_fs_.promises; -const SUMMARY_ENV_VAR = 'GITHUB_STEP_SUMMARY'; -const SUMMARY_DOCS_URL = 'https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary'; -class Summary { - constructor() { - this._buffer = ''; + else { + javaVersionRegExp = /(?(?<=(^|\s|-))(\d+\S*))(\s|$)/; } - /** - * Finds the summary file path from the environment, rejects if env var is not found or file does not exist - * Also checks r/w permissions. - * - * @returns step summary file path - */ - filePath() { - return summary_awaiter(this, void 0, void 0, function* () { - if (this._filePath) { - return this._filePath; - } - const pathFromEnv = process.env[SUMMARY_ENV_VAR]; - if (!pathFromEnv) { - throw new Error(`Unable to find environment variable for $${SUMMARY_ENV_VAR}. Check if your runtime environment supports job summaries.`); - } - try { - yield access(pathFromEnv, external_fs_.constants.R_OK | external_fs_.constants.W_OK); - } - catch (_a) { - throw new Error(`Unable to access summary file: '${pathFromEnv}'. Check if the file has correct read/write permissions.`); - } - this._filePath = pathFromEnv; - return this._filePath; - }); + 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}'`); } - /** - * Wraps content in an HTML tag, adding any HTML attributes - * - * @param {string} tag HTML tag to wrap - * @param {string | null} content content within the tag - * @param {[attribute: string]: string} attrs key-value list of HTML attributes to add - * - * @returns {string} content wrapped in HTML element - */ - wrap(tag, content, attrs = {}) { - const htmlAttrs = Object.entries(attrs) - .map(([key, value]) => ` ${key}="${value}"`) - .join(''); - if (!content) { - return `<${tag}${htmlAttrs}>`; + // 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}'`); } - return `<${tag}${htmlAttrs}>${content}`; } - /** - * Writes text in the buffer to the summary buffer file and empties buffer. Will append by default. - * - * @param {SummaryWriteOptions} [options] (optional) options for write operation - * - * @returns {Promise} summary instance - */ - write(options) { - return summary_awaiter(this, void 0, void 0, function* () { - const overwrite = !!(options === null || options === void 0 ? void 0 : options.overwrite); - const filePath = yield this.filePath(); - const writeFunc = overwrite ? writeFile : appendFile; - yield writeFunc(filePath, this._buffer, { encoding: 'utf8' }); - return this.emptyBuffer(); - }); + core.debug(`Parsed version '${capturedVersion}' from file '${versionFileName}'`); + if (!capturedVersion) { + return null; } - /** - * Clears the summary buffer and wipes the summary file - * - * @returns {Summary} summary instance - */ - clear() { - return summary_awaiter(this, void 0, void 0, function* () { - return this.emptyBuffer().write({ overwrite: true }); - }); + 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; } - /** - * Returns the current summary buffer as a string - * - * @returns {string} string of summary buffer - */ - stringify() { - return this._buffer; + // 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(); } - /** - * If the summary buffer is empty - * - * @returns {boolen} true if the buffer is empty - */ - isEmptyBuffer() { - return this._buffer.length === 0; + 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.`); } - /** - * Resets the summary buffer without writing to summary file - * - * @returns {Summary} summary instance - */ - emptyBuffer() { - this._buffer = ''; - return this; + 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'; } - /** - * Adds raw text to the summary buffer - * - * @param {string} text content to add - * @param {boolean} [addEOL=false] (optional) append an EOL to the raw text (default: false) - * - * @returns {Summary} summary instance - */ - addRaw(text, addEOL = false) { - this._buffer += text; - return addEOL ? this.addEOL() : this; + if (normalized.startsWith('oracle-graalvm')) { + return 'graalvm'; } - /** - * Adds the operating system-specific end-of-line marker to the buffer - * - * @returns {Summary} summary instance - */ - addEOL() { - return this.addRaw(external_os_.EOL); + 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.`); } - /** - * Adds an HTML codeblock to the summary buffer - * - * @param {string} code content to render within fenced code block - * @param {string} lang (optional) language to syntax highlight code - * - * @returns {Summary} summary instance - */ - addCodeBlock(code, lang) { - const attrs = Object.assign({}, (lang && { lang })); - const element = this.wrap('pre', this.wrap('code', code), attrs); - return this.addRaw(element).addEOL(); + 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('.')}`; } - /** - * Adds an HTML list to the summary buffer - * - * @param {string[]} items list of items to render - * @param {boolean} [ordered=false] (optional) if the rendered list should be ordered or not (default: false) - * - * @returns {Summary} summary instance - */ - addList(items, ordered = false) { - const tag = ordered ? 'ol' : 'ul'; - const listItems = items.map(item => this.wrap('li', item)).join(''); - const element = this.wrap(tag, listItems); - return this.addRaw(element).addEOL(); + 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; } - /** - * Adds an HTML table to the summary buffer - * - * @param {SummaryTableCell[]} rows table rows - * - * @returns {Summary} summary instance - */ - addTable(rows) { - const tableBody = rows - .map(row => { - const cells = row - .map(cell => { - if (typeof cell === 'string') { - return this.wrap('td', cell); - } - const { header, data, colspan, rowspan } = cell; - const tag = header ? 'th' : 'td'; - const attrs = Object.assign(Object.assign({}, (colspan && { colspan })), (rowspan && { rowspan })); - return this.wrap(tag, data, attrs); - }) - .join(''); - return this.wrap('tr', cells); - }) - .join(''); - const element = this.wrap('table', tableBody); - return this.addRaw(element).addEOL(); + return headers; +} +const MAX_PAGINATION_PAGES = 1000; +function getNextPageUrlFromLinkHeader(headers) { + if (!headers) { + return null; } - /** - * Adds a collapsable HTML details element to the summary buffer - * - * @param {string} label text for the closed state - * @param {string} content collapsable content - * - * @returns {Summary} summary instance - */ - addDetails(label, content) { - const element = this.wrap('details', this.wrap('summary', label) + content); - return this.addRaw(element).addEOL(); + const linkHeader = headers.link ?? headers.Link; + if (!linkHeader) { + return null; } - /** - * Adds an HTML image tag to the summary buffer - * - * @param {string} src path to the image you to embed - * @param {string} alt text description of the image - * @param {SummaryImageOptions} options (optional) addition image attributes - * - * @returns {Summary} summary instance - */ - addImage(src, alt, options) { - const { width, height } = options || {}; - const attrs = Object.assign(Object.assign({}, (width && { width })), (height && { height })); - const element = this.wrap('img', null, Object.assign({ src, alt }, attrs)); - return this.addRaw(element).addEOL(); + 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]; + } } - /** - * Adds an HTML section heading element - * - * @param {string} text heading text - * @param {number | string} [level=1] (optional) the heading level, default: 1 - * - * @returns {Summary} summary instance - */ - addHeading(text, level) { - const tag = `h${level}`; - const allowedTag = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(tag) - ? tag - : 'h1'; - const element = this.wrap(allowedTag, text); - return this.addRaw(element).addEOL(); - } - /** - * Adds an HTML thematic break (
) to the summary buffer - * - * @returns {Summary} summary instance - */ - addSeparator() { - const element = this.wrap('hr', null); - return this.addRaw(element).addEOL(); - } - /** - * Adds an HTML line break (
) to the summary buffer - * - * @returns {Summary} summary instance - */ - addBreak() { - const element = this.wrap('br', null); - return this.addRaw(element).addEOL(); - } - /** - * Adds an HTML blockquote to the summary buffer - * - * @param {string} text quote text - * @param {string} cite (optional) citation url - * - * @returns {Summary} summary instance - */ - addQuote(text, cite) { - const attrs = Object.assign({}, (cite && { cite })); - const element = this.wrap('blockquote', text, attrs); - return this.addRaw(element).addEOL(); + return null; +} +function validatePaginationUrl(url, allowedOrigin) { + try { + const parsed = new URL(url); + const allowed = new URL(allowedOrigin); + return parsed.origin === allowed.origin; } - /** - * Adds an HTML anchor tag to the summary buffer - * - * @param {string} text link text/content - * @param {string} href hyperlink - * - * @returns {Summary} summary instance - */ - addLink(text, href) { - const element = this.wrap('a', text, { href }); - return this.addRaw(element).addEOL(); + catch { + return false; } } -const _summary = new Summary(); -/** - * @deprecated use `core.summary` - */ -const markdownSummary = (/* unused pure expression or super */ null && (_summary)); -const summary = (/* unused pure expression or super */ null && (_summary)); -//# sourceMappingURL=summary.js.map -;// CONCATENATED MODULE: ./node_modules/@actions/core/lib/path-utils.js - -/** - * toPosixPath converts the given path to the posix form. On Windows, \\ will be - * replaced with /. - * - * @param pth. Path to transform. - * @return string Posix path. - */ -function toPosixPath(pth) { - return pth.replace(/[\\]/g, '/'); -} -/** - * toWin32Path converts the given path to the win32 form. On Linux, / will be - * replaced with \\. - * - * @param pth. Path to transform. - * @return string Win32 path. - */ -function toWin32Path(pth) { - return pth.replace(/[/]/g, '\\'); +// 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; } -/** - * toPlatformPath converts the given path to a platform-specific path. It does - * this by replacing instances of / and \ with the platform-specific path - * separator. - * - * @param pth The path to platformize. - * @return string The platform-specific path. - */ -function toPlatformPath(pth) { - return pth.replace(/[/\\]/g, path.sep); +// 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); } -//# sourceMappingURL=path-utils.js.map -// EXTERNAL MODULE: ./node_modules/@actions/exec/lib/exec.js + 2 modules -var lib_exec = __nccwpck_require__(5260); -;// CONCATENATED MODULE: ./node_modules/@actions/core/lib/platform.js -var platform_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -const getWindowsInfo = () => platform_awaiter(void 0, void 0, void 0, function* () { - const { stdout: version } = yield exec.getExecOutput('powershell -command "(Get-CimInstance -ClassName Win32_OperatingSystem).Version"', undefined, { - silent: true - }); - const { stdout: name } = yield exec.getExecOutput('powershell -command "(Get-CimInstance -ClassName Win32_OperatingSystem).Caption"', undefined, { - silent: true - }); - return { - name: name.trim(), - version: version.trim() - }; -}); -const getMacOsInfo = () => platform_awaiter(void 0, void 0, void 0, function* () { - var _a, _b, _c, _d; - const { stdout } = yield exec.getExecOutput('sw_vers', undefined, { - silent: true - }); - const version = (_b = (_a = stdout.match(/ProductVersion:\s*(.+)/)) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : ''; - const name = (_d = (_c = stdout.match(/ProductName:\s*(.+)/)) === null || _c === void 0 ? void 0 : _c[1]) !== null && _d !== void 0 ? _d : ''; - return { - name, - version - }; -}); -const getLinuxInfo = () => platform_awaiter(void 0, void 0, void 0, function* () { - const { stdout } = yield exec.getExecOutput('lsb_release', ['-i', '-r', '-s'], { - silent: true - }); - const [name, version] = stdout.trim().split('\n'); - return { - name, - version - }; -}); -const platform = external_os_.platform(); -const arch = external_os_.arch(); -const isWindows = platform === 'win32'; -const isMacOS = platform === 'darwin'; -const isLinux = platform === 'linux'; -function getDetails() { - return platform_awaiter(this, void 0, void 0, function* () { - return Object.assign(Object.assign({}, (yield (isWindows - ? getWindowsInfo() - : isMacOS - ? getMacOsInfo() - : getLinuxInfo()))), { platform, - arch, - isWindows, - isMacOS, - isLinux }); - }); -} -//# sourceMappingURL=platform.js.map -;// CONCATENATED MODULE: ./node_modules/@actions/core/lib/core.js -var core_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; +/***/ }), +/***/ 2613: +/***/ ((module) => { +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("assert"); +/***/ }), +/***/ 181: +/***/ ((module) => { +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("buffer"); -/** - * The code to exit an action - */ -var ExitCode; -(function (ExitCode) { - /** - * A code indicating that the action was successful - */ - ExitCode[ExitCode["Success"] = 0] = "Success"; - /** - * A code indicating that the action was a failure - */ - ExitCode[ExitCode["Failure"] = 1] = "Failure"; -})(ExitCode || (ExitCode = {})); -//----------------------------------------------------------------------- -// Variables -//----------------------------------------------------------------------- -/** - * Sets env variable for this action and future actions in the job - * @param name the name of the variable to set - * @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function exportVariable(name, val) { - const convertedVal = toCommandValue(val); - process.env[name] = convertedVal; - const filePath = process.env['GITHUB_ENV'] || ''; - if (filePath) { - return issueFileCommand('ENV', prepareKeyValueMessage(name, val)); - } - issueCommand('set-env', { name }, convertedVal); -} -/** - * Registers a secret which will get masked from logs - * - * @param secret - Value of the secret to be masked - * @remarks - * This function instructs the Actions runner to mask the specified value in any - * logs produced during the workflow run. Once registered, the secret value will - * be replaced with asterisks (***) whenever it appears in console output, logs, - * or error messages. - * - * This is useful for protecting sensitive information such as: - * - API keys - * - Access tokens - * - Authentication credentials - * - URL parameters containing signatures (SAS tokens) - * - * Note that masking only affects future logs; any previous appearances of the - * secret in logs before calling this function will remain unmasked. - * - * @example - * ```typescript - * // Register an API token as a secret - * const apiToken = "abc123xyz456"; - * setSecret(apiToken); - * - * // Now any logs containing this value will show *** instead - * console.log(`Using token: ${apiToken}`); // Outputs: "Using token: ***" - * ``` - */ -function core_setSecret(secret) { - command_issueCommand('add-mask', {}, secret); -} -/** - * Prepends inputPath to the PATH (for this action and future actions) - * @param inputPath - */ -function addPath(inputPath) { - const filePath = process.env['GITHUB_PATH'] || ''; - if (filePath) { - issueFileCommand('PATH', inputPath); - } - else { - issueCommand('add-path', {}, inputPath); - } - process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}`; -} -/** - * Gets the value of an input. - * Unless trimWhitespace is set to false in InputOptions, the value is also trimmed. - * Returns an empty string if the value is not defined. - * - * @param name name of the input to get - * @param options optional. See InputOptions. - * @returns string - */ -function getInput(name, options) { - const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || ''; - if (options && options.required && !val) { - throw new Error(`Input required and not supplied: ${name}`); - } - if (options && options.trimWhitespace === false) { - return val; - } - return val.trim(); -} -/** - * Gets the values of an multiline input. Each value is also trimmed. - * - * @param name name of the input to get - * @param options optional. See InputOptions. - * @returns string[] - * - */ -function getMultilineInput(name, options) { - const inputs = getInput(name, options) - .split('\n') - .filter(x => x !== ''); - if (options && options.trimWhitespace === false) { - return inputs; - } - return inputs.map(input => input.trim()); -} -/** - * Gets the input value of the boolean type in the YAML 1.2 "core schema" specification. - * Support boolean input list: `true | True | TRUE | false | False | FALSE` . - * The return value is also in boolean type. - * ref: https://yaml.org/spec/1.2/spec.html#id2804923 - * - * @param name name of the input to get - * @param options optional. See InputOptions. - * @returns boolean - */ -function getBooleanInput(name, options) { - const trueValue = ['true', 'True', 'TRUE']; - const falseValue = ['false', 'False', 'FALSE']; - const val = getInput(name, options); - if (trueValue.includes(val)) - return true; - if (falseValue.includes(val)) - return false; - throw new TypeError(`Input does not meet YAML 1.2 "Core Schema" specification: ${name}\n` + - `Support boolean input list: \`true | True | TRUE | false | False | FALSE\``); -} -/** - * Sets the value of an output. - * - * @param name name of the output to set - * @param value value to store. Non-string values will be converted to a string via JSON.stringify - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function setOutput(name, value) { - const filePath = process.env['GITHUB_OUTPUT'] || ''; - if (filePath) { - return issueFileCommand('OUTPUT', prepareKeyValueMessage(name, value)); - } - process.stdout.write(os.EOL); - issueCommand('set-output', { name }, toCommandValue(value)); -} -/** - * Enables or disables the echoing of commands into stdout for the rest of the step. - * Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set. - * - */ -function setCommandEcho(enabled) { - issue('echo', enabled ? 'on' : 'off'); -} -//----------------------------------------------------------------------- -// Results -//----------------------------------------------------------------------- -/** - * Sets the action status to failed. - * When the action exits it will be with an exit code of 1 - * @param message add error issue message - */ -function setFailed(message) { - process.exitCode = ExitCode.Failure; - error(message); -} -//----------------------------------------------------------------------- -// Logging Commands -//----------------------------------------------------------------------- -/** - * Gets whether Actions Step Debug is on or not - */ -function isDebug() { - return process.env['RUNNER_DEBUG'] === '1'; -} -/** - * Writes debug message to user log - * @param message debug message - */ -function core_debug(message) { - command_issueCommand('debug', {}, message); -} -/** - * Adds an error issue - * @param message error issue message. Errors will be converted to string via toString() - * @param properties optional properties to add to the annotation. - */ -function error(message, properties = {}) { - command_issueCommand('error', utils_toCommandProperties(properties), message instanceof Error ? message.toString() : message); -} -/** - * Adds a warning issue - * @param message warning issue message. Errors will be converted to string via toString() - * @param properties optional properties to add to the annotation. - */ -function warning(message, properties = {}) { - command_issueCommand('warning', utils_toCommandProperties(properties), message instanceof Error ? message.toString() : message); -} -/** - * Adds a notice issue - * @param message notice issue message. Errors will be converted to string via toString() - * @param properties optional properties to add to the annotation. - */ -function notice(message, properties = {}) { - issueCommand('notice', toCommandProperties(properties), message instanceof Error ? message.toString() : message); -} -/** - * Writes info to log with console.log. - * @param message info message - */ -function info(message) { - process.stdout.write(message + external_os_.EOL); -} -/** - * Begin an output group. - * - * Output until the next `groupEnd` will be foldable in this group - * - * @param name The name of the output group - */ -function startGroup(name) { - issue('group', name); -} -/** - * End an output group. - */ -function endGroup() { - issue('endgroup'); -} -/** - * Wrap an asynchronous function call in a group. - * - * Returns the same type as the function itself. - * - * @param name The name of the group - * @param fn The function to wrap in the group - */ -function group(name, fn) { - return core_awaiter(this, void 0, void 0, function* () { - startGroup(name); - let result; - try { - result = yield fn(); - } - finally { - endGroup(); - } - return result; - }); -} -//----------------------------------------------------------------------- -// Wrapper action state -//----------------------------------------------------------------------- -/** - * Saves state for current action, the state can only be retrieved by this action's post job execution. - * - * @param name name of the state to store - * @param value value to store. Non-string values will be converted to a string via JSON.stringify - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function saveState(name, value) { - const filePath = process.env['GITHUB_STATE'] || ''; - if (filePath) { - return issueFileCommand('STATE', prepareKeyValueMessage(name, value)); - } - issueCommand('save-state', { name }, toCommandValue(value)); -} -/** - * Gets the value of an state set by this action's main execution. - * - * @param name name of the state to get - * @returns string - */ -function getState(name) { - return process.env[`STATE_${name}`] || ''; -} -function getIDToken(aud) { - return core_awaiter(this, void 0, void 0, function* () { - return yield OidcClient.getIDToken(aud); - }); -} -/** - * Summary exports - */ +/***/ }), -/** - * @deprecated use core.summary - */ +/***/ 5317: +/***/ ((module) => { -/** - * Path exports - */ +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("child_process"); -/** - * Platform utilities exports - */ +/***/ }), -//# sourceMappingURL=core.js.map +/***/ 6982: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("crypto"); /***/ }), -/***/ 5260: -/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __nccwpck_require__) => { +/***/ 4434: +/***/ ((module) => { +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("events"); -// EXPORTS -__nccwpck_require__.d(__webpack_exports__, { - m: () => (/* binding */ exec) -}); +/***/ }), -// UNUSED EXPORTS: getExecOutput +/***/ 9896: +/***/ ((module) => { -// EXTERNAL MODULE: external "string_decoder" -var external_string_decoder_ = __nccwpck_require__(3193); -// EXTERNAL MODULE: external "os" -var external_os_ = __nccwpck_require__(857); -// EXTERNAL MODULE: external "events" -var external_events_ = __nccwpck_require__(4434); -// EXTERNAL MODULE: external "child_process" -var external_child_process_ = __nccwpck_require__(5317); -// EXTERNAL MODULE: external "path" -var external_path_ = __nccwpck_require__(6928); -// EXTERNAL MODULE: ./node_modules/@actions/io/lib/io.js -var io = __nccwpck_require__(8701); -// EXTERNAL MODULE: ./node_modules/@actions/io/lib/io-util.js -var io_util = __nccwpck_require__(90); -;// CONCATENATED MODULE: external "timers" -const external_timers_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("timers"); -;// CONCATENATED MODULE: ./node_modules/@actions/exec/lib/toolrunner.js -var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("fs"); +/***/ }), +/***/ 8611: +/***/ ((module) => { +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("http"); +/***/ }), +/***/ 5692: +/***/ ((module) => { +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("https"); -/* eslint-disable @typescript-eslint/unbound-method */ -const IS_WINDOWS = process.platform === 'win32'; -/* - * Class for running command line tools. Handles quoting and arg parsing in a platform agnostic way. - */ -class ToolRunner extends external_events_.EventEmitter { - constructor(toolPath, args, options) { - super(); - if (!toolPath) { - throw new Error("Parameter 'toolPath' cannot be null or empty."); - } - this.toolPath = toolPath; - this.args = args || []; - this.options = options || {}; - } - _debug(message) { - if (this.options.listeners && this.options.listeners.debug) { - this.options.listeners.debug(message); - } - } - _getCommandString(options, noPrefix) { - const toolPath = this._getSpawnFileName(); - const args = this._getSpawnArgs(options); - let cmd = noPrefix ? '' : '[command]'; // omit prefix when piped to a second tool - if (IS_WINDOWS) { - // Windows + cmd file - if (this._isCmdFile()) { - cmd += toolPath; - for (const a of args) { - cmd += ` ${a}`; - } - } - // Windows + verbatim - else if (options.windowsVerbatimArguments) { - cmd += `"${toolPath}"`; - for (const a of args) { - cmd += ` ${a}`; - } - } - // Windows (regular) - else { - cmd += this._windowsQuoteCmdArg(toolPath); - for (const a of args) { - cmd += ` ${this._windowsQuoteCmdArg(a)}`; - } - } - } - else { - // OSX/Linux - this can likely be improved with some form of quoting. - // creating processes on Unix is fundamentally different than Windows. - // on Unix, execvp() takes an arg array. - cmd += toolPath; - for (const a of args) { - cmd += ` ${a}`; - } - } - return cmd; - } - _processLineBuffer(data, strBuffer, onLine) { - try { - let s = strBuffer + data.toString(); - let n = s.indexOf(external_os_.EOL); - while (n > -1) { - const line = s.substring(0, n); - onLine(line); - // the rest of the string ... - s = s.substring(n + external_os_.EOL.length); - n = s.indexOf(external_os_.EOL); - } - return s; - } - catch (err) { - // streaming lines to console is best effort. Don't fail a build. - this._debug(`error processing line. Failed with error ${err}`); - return ''; - } - } - _getSpawnFileName() { - if (IS_WINDOWS) { - if (this._isCmdFile()) { - return process.env['COMSPEC'] || 'cmd.exe'; - } - } - return this.toolPath; - } - _getSpawnArgs(options) { - if (IS_WINDOWS) { - if (this._isCmdFile()) { - let argline = `/D /S /C "${this._windowsQuoteCmdArg(this.toolPath)}`; - for (const a of this.args) { - argline += ' '; - argline += options.windowsVerbatimArguments - ? a - : this._windowsQuoteCmdArg(a); - } - argline += '"'; - return [argline]; - } - } - return this.args; - } - _endsWith(str, end) { - return str.endsWith(end); - } - _isCmdFile() { - const upperToolPath = this.toolPath.toUpperCase(); - return (this._endsWith(upperToolPath, '.CMD') || - this._endsWith(upperToolPath, '.BAT')); - } - _windowsQuoteCmdArg(arg) { - // for .exe, apply the normal quoting rules that libuv applies - if (!this._isCmdFile()) { - return this._uvQuoteCmdArg(arg); - } - // otherwise apply quoting rules specific to the cmd.exe command line parser. - // the libuv rules are generic and are not designed specifically for cmd.exe - // command line parser. - // - // for a detailed description of the cmd.exe command line parser, refer to - // http://stackoverflow.com/questions/4094699/how-does-the-windows-command-interpreter-cmd-exe-parse-scripts/7970912#7970912 - // need quotes for empty arg - if (!arg) { - return '""'; - } - // determine whether the arg needs to be quoted - const cmdSpecialChars = [ - ' ', - '\t', - '&', - '(', - ')', - '[', - ']', - '{', - '}', - '^', - '=', - ';', - '!', - "'", - '+', - ',', - '`', - '~', - '|', - '<', - '>', - '"' - ]; - let needsQuotes = false; - for (const char of arg) { - if (cmdSpecialChars.some(x => x === char)) { - needsQuotes = true; - break; - } - } - // short-circuit if quotes not needed - if (!needsQuotes) { - return arg; - } - // the following quoting rules are very similar to the rules that by libuv applies. - // - // 1) wrap the string in quotes - // - // 2) double-up quotes - i.e. " => "" - // - // this is different from the libuv quoting rules. libuv replaces " with \", which unfortunately - // doesn't work well with a cmd.exe command line. - // - // note, replacing " with "" also works well if the arg is passed to a downstream .NET console app. - // for example, the command line: - // foo.exe "myarg:""my val""" - // is parsed by a .NET console app into an arg array: - // [ "myarg:\"my val\"" ] - // which is the same end result when applying libuv quoting rules. although the actual - // command line from libuv quoting rules would look like: - // foo.exe "myarg:\"my val\"" - // - // 3) double-up slashes that precede a quote, - // e.g. hello \world => "hello \world" - // hello\"world => "hello\\""world" - // hello\\"world => "hello\\\\""world" - // hello world\ => "hello world\\" - // - // technically this is not required for a cmd.exe command line, or the batch argument parser. - // the reasons for including this as a .cmd quoting rule are: - // - // a) this is optimized for the scenario where the argument is passed from the .cmd file to an - // external program. many programs (e.g. .NET console apps) rely on the slash-doubling rule. - // - // b) it's what we've been doing previously (by deferring to node default behavior) and we - // haven't heard any complaints about that aspect. - // - // note, a weakness of the quoting rules chosen here, is that % is not escaped. in fact, % cannot be - // escaped when used on the command line directly - even though within a .cmd file % can be escaped - // by using %%. - // - // the saving grace is, on the command line, %var% is left as-is if var is not defined. this contrasts - // the line parsing rules within a .cmd file, where if var is not defined it is replaced with nothing. - // - // one option that was explored was replacing % with ^% - i.e. %var% => ^%var^%. this hack would - // often work, since it is unlikely that var^ would exist, and the ^ character is removed when the - // variable is used. the problem, however, is that ^ is not removed when %* is used to pass the args - // to an external program. - // - // an unexplored potential solution for the % escaping problem, is to create a wrapper .cmd file. - // % can be escaped within a .cmd file. - let reverse = '"'; - let quoteHit = true; - for (let i = arg.length; i > 0; i--) { - // walk the string in reverse - reverse += arg[i - 1]; - if (quoteHit && arg[i - 1] === '\\') { - reverse += '\\'; // double the slash +/***/ }), + +/***/ 9278: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("net"); + +/***/ }), + +/***/ 4589: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:assert"); + +/***/ }), + +/***/ 6698: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:async_hooks"); + +/***/ }), + +/***/ 4573: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:buffer"); + +/***/ }), + +/***/ 7540: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:console"); + +/***/ }), + +/***/ 7598: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:crypto"); + +/***/ }), + +/***/ 3053: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:diagnostics_channel"); + +/***/ }), + +/***/ 610: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:dns"); + +/***/ }), + +/***/ 8474: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:events"); + +/***/ }), + +/***/ 3024: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:fs"); + +/***/ }), + +/***/ 7067: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:http"); + +/***/ }), + +/***/ 2467: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:http2"); + +/***/ }), + +/***/ 4708: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:https"); + +/***/ }), + +/***/ 8995: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:module"); + +/***/ }), + +/***/ 7030: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:net"); + +/***/ }), + +/***/ 8161: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:os"); + +/***/ }), + +/***/ 6760: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:path"); + +/***/ }), + +/***/ 643: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:perf_hooks"); + +/***/ }), + +/***/ 1708: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:process"); + +/***/ }), + +/***/ 1792: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:querystring"); + +/***/ }), + +/***/ 7075: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:stream"); + +/***/ }), + +/***/ 1692: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:tls"); + +/***/ }), + +/***/ 3136: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:url"); + +/***/ }), + +/***/ 7975: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:util"); + +/***/ }), + +/***/ 3429: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:util/types"); + +/***/ }), + +/***/ 5919: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:worker_threads"); + +/***/ }), + +/***/ 8522: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:zlib"); + +/***/ }), + +/***/ 857: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("os"); + +/***/ }), + +/***/ 6928: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("path"); + +/***/ }), + +/***/ 2203: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("stream"); + +/***/ }), + +/***/ 3193: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("string_decoder"); + +/***/ }), + +/***/ 4756: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("tls"); + +/***/ }), + +/***/ 2018: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("tty"); + +/***/ }), + +/***/ 7016: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("url"); + +/***/ }), + +/***/ 9023: +/***/ ((module) => { + +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("util"); + +/***/ }), + +/***/ 3838: +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __nccwpck_require__) => { + + +// EXPORTS +__nccwpck_require__.d(__webpack_exports__, { + Yz: () => (/* binding */ core_debug), + z3: () => (/* binding */ error), + V4: () => (/* binding */ getInput), + Gu: () => (/* binding */ getState), + pq: () => (/* binding */ info), + _o: () => (/* binding */ isDebug), + C1: () => (/* binding */ setFailed), + Pq: () => (/* binding */ core_setSecret), + $e: () => (/* binding */ warning) +}); + +// UNUSED EXPORTS: ExitCode, addPath, endGroup, exportVariable, getBooleanInput, getIDToken, getMultilineInput, group, markdownSummary, notice, platform, saveState, setCommandEcho, setOutput, startGroup, summary, toPlatformPath, toPosixPath, toWin32Path + +// EXTERNAL MODULE: external "os" +var external_os_ = __nccwpck_require__(857); +;// CONCATENATED MODULE: ./node_modules/@actions/core/lib/utils.js +// We use any as a valid input type +/* eslint-disable @typescript-eslint/no-explicit-any */ +/** + * Sanitizes an input into a string so it can be passed into issueCommand safely + * @param input input to sanitize into a string + */ +function utils_toCommandValue(input) { + if (input === null || input === undefined) { + return ''; + } + else if (typeof input === 'string' || input instanceof String) { + return input; + } + return JSON.stringify(input); +} +/** + * + * @param annotationProperties + * @returns The command properties to send with the actual annotation command + * See IssueCommandProperties: https://github.com/actions/runner/blob/main/src/Runner.Worker/ActionCommandManager.cs#L646 + */ +function utils_toCommandProperties(annotationProperties) { + if (!Object.keys(annotationProperties).length) { + return {}; + } + return { + title: annotationProperties.title, + file: annotationProperties.file, + line: annotationProperties.startLine, + endLine: annotationProperties.endLine, + col: annotationProperties.startColumn, + endColumn: annotationProperties.endColumn + }; +} +//# sourceMappingURL=utils.js.map +;// CONCATENATED MODULE: ./node_modules/@actions/core/lib/command.js + + +/** + * Issues a command to the GitHub Actions runner + * + * @param command - The command name to issue + * @param properties - Additional properties for the command (key-value pairs) + * @param message - The message to include with the command + * @remarks + * This function outputs a specially formatted string to stdout that the Actions + * runner interprets as a command. These commands can control workflow behavior, + * set outputs, create annotations, mask values, and more. + * + * Command Format: + * ::name key=value,key=value::message + * + * @example + * ```typescript + * // Issue a warning annotation + * issueCommand('warning', {}, 'This is a warning message'); + * // Output: ::warning::This is a warning message + * + * // Set an environment variable + * issueCommand('set-env', { name: 'MY_VAR' }, 'some value'); + * // Output: ::set-env name=MY_VAR::some value + * + * // Add a secret mask + * issueCommand('add-mask', {}, 'secretValue123'); + * // Output: ::add-mask::secretValue123 + * ``` + * + * @internal + * This is an internal utility function that powers the public API functions + * such as setSecret, warning, error, and exportVariable. + */ +function command_issueCommand(command, properties, message) { + const cmd = new Command(command, properties, message); + process.stdout.write(cmd.toString() + external_os_.EOL); +} +function command_issue(name, message = '') { + command_issueCommand(name, {}, message); +} +const CMD_STRING = '::'; +class Command { + constructor(command, properties, message) { + if (!command) { + command = 'missing.command'; + } + this.command = command; + this.properties = properties; + this.message = message; + } + toString() { + let cmdStr = CMD_STRING + this.command; + if (this.properties && Object.keys(this.properties).length > 0) { + cmdStr += ' '; + let first = true; + for (const key in this.properties) { + if (this.properties.hasOwnProperty(key)) { + const val = this.properties[key]; + if (val) { + if (first) { + first = false; + } + else { + cmdStr += ','; + } + cmdStr += `${key}=${escapeProperty(val)}`; + } + } + } + } + cmdStr += `${CMD_STRING}${escapeData(this.message)}`; + return cmdStr; + } +} +function escapeData(s) { + return utils_toCommandValue(s) + .replace(/%/g, '%25') + .replace(/\r/g, '%0D') + .replace(/\n/g, '%0A'); +} +function escapeProperty(s) { + return utils_toCommandValue(s) + .replace(/%/g, '%25') + .replace(/\r/g, '%0D') + .replace(/\n/g, '%0A') + .replace(/:/g, '%3A') + .replace(/,/g, '%2C'); +} +//# sourceMappingURL=command.js.map +// EXTERNAL MODULE: external "crypto" +var external_crypto_ = __nccwpck_require__(6982); +// EXTERNAL MODULE: external "fs" +var external_fs_ = __nccwpck_require__(9896); +;// CONCATENATED MODULE: ./node_modules/@actions/core/lib/file-command.js +// For internal use, subject to change. +// We use any as a valid input type +/* eslint-disable @typescript-eslint/no-explicit-any */ + + + + +function file_command_issueFileCommand(command, message) { + const filePath = process.env[`GITHUB_${command}`]; + if (!filePath) { + throw new Error(`Unable to find environment variable for file command ${command}`); + } + if (!fs.existsSync(filePath)) { + throw new Error(`Missing file at path: ${filePath}`); + } + fs.appendFileSync(filePath, `${toCommandValue(message)}${os.EOL}`, { + encoding: 'utf8' + }); +} +function file_command_prepareKeyValueMessage(key, value) { + const delimiter = `ghadelimiter_${crypto.randomUUID()}`; + const convertedValue = toCommandValue(value); + // These should realistically never happen, but just in case someone finds a + // way to exploit uuid generation let's not allow keys or values that contain + // the delimiter. + if (key.includes(delimiter)) { + throw new Error(`Unexpected input: name should not contain the delimiter "${delimiter}"`); + } + if (convertedValue.includes(delimiter)) { + throw new Error(`Unexpected input: value should not contain the delimiter "${delimiter}"`); + } + return `${key}<<${delimiter}${os.EOL}${convertedValue}${os.EOL}${delimiter}`; +} +//# sourceMappingURL=file-command.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: ./node_modules/@actions/http-client/lib/auth.js +var auth = __nccwpck_require__(2145); +;// CONCATENATED MODULE: ./node_modules/@actions/core/lib/oidc-utils.js +var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; + + + +class oidc_utils_OidcClient { + static createHttpClient(allowRetry = true, maxRetry = 10) { + const requestOptions = { + allowRetries: allowRetry, + maxRetries: maxRetry + }; + return new HttpClient('actions/oidc-client', [new BearerCredentialHandler(oidc_utils_OidcClient.getRequestToken())], requestOptions); + } + static getRequestToken() { + const token = process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN']; + if (!token) { + throw new Error('Unable to get ACTIONS_ID_TOKEN_REQUEST_TOKEN env variable'); + } + return token; + } + static getIDTokenUrl() { + const runtimeUrl = process.env['ACTIONS_ID_TOKEN_REQUEST_URL']; + if (!runtimeUrl) { + throw new Error('Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable'); + } + return runtimeUrl; + } + static getCall(id_token_url) { + return __awaiter(this, void 0, void 0, function* () { + var _a; + const httpclient = oidc_utils_OidcClient.createHttpClient(); + const res = yield httpclient + .getJson(id_token_url) + .catch(error => { + throw new Error(`Failed to get ID Token. \n + Error Code : ${error.statusCode}\n + Error Message: ${error.message}`); + }); + const id_token = (_a = res.result) === null || _a === void 0 ? void 0 : _a.value; + if (!id_token) { + throw new Error('Response json body do not have ID Token field'); + } + return id_token; + }); + } + static getIDToken(audience) { + return __awaiter(this, void 0, void 0, function* () { + try { + // New ID Token is requested from action service + let id_token_url = oidc_utils_OidcClient.getIDTokenUrl(); + if (audience) { + const encodedAudience = encodeURIComponent(audience); + id_token_url = `${id_token_url}&audience=${encodedAudience}`; + } + debug(`ID token url is ${id_token_url}`); + const id_token = yield oidc_utils_OidcClient.getCall(id_token_url); + setSecret(id_token); + return id_token; + } + catch (error) { + throw new Error(`Error message: ${error.message}`); + } + }); + } +} +//# sourceMappingURL=oidc-utils.js.map +;// CONCATENATED MODULE: ./node_modules/@actions/core/lib/summary.js +var summary_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; + + +const { access, appendFile, writeFile } = external_fs_.promises; +const SUMMARY_ENV_VAR = 'GITHUB_STEP_SUMMARY'; +const SUMMARY_DOCS_URL = 'https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary'; +class Summary { + constructor() { + this._buffer = ''; + } + /** + * Finds the summary file path from the environment, rejects if env var is not found or file does not exist + * Also checks r/w permissions. + * + * @returns step summary file path + */ + filePath() { + return summary_awaiter(this, void 0, void 0, function* () { + if (this._filePath) { + return this._filePath; + } + const pathFromEnv = process.env[SUMMARY_ENV_VAR]; + if (!pathFromEnv) { + throw new Error(`Unable to find environment variable for $${SUMMARY_ENV_VAR}. Check if your runtime environment supports job summaries.`); + } + try { + yield access(pathFromEnv, external_fs_.constants.R_OK | external_fs_.constants.W_OK); + } + catch (_a) { + throw new Error(`Unable to access summary file: '${pathFromEnv}'. Check if the file has correct read/write permissions.`); + } + this._filePath = pathFromEnv; + return this._filePath; + }); + } + /** + * Wraps content in an HTML tag, adding any HTML attributes + * + * @param {string} tag HTML tag to wrap + * @param {string | null} content content within the tag + * @param {[attribute: string]: string} attrs key-value list of HTML attributes to add + * + * @returns {string} content wrapped in HTML element + */ + wrap(tag, content, attrs = {}) { + const htmlAttrs = Object.entries(attrs) + .map(([key, value]) => ` ${key}="${value}"`) + .join(''); + if (!content) { + return `<${tag}${htmlAttrs}>`; + } + return `<${tag}${htmlAttrs}>${content}`; + } + /** + * Writes text in the buffer to the summary buffer file and empties buffer. Will append by default. + * + * @param {SummaryWriteOptions} [options] (optional) options for write operation + * + * @returns {Promise} summary instance + */ + write(options) { + return summary_awaiter(this, void 0, void 0, function* () { + const overwrite = !!(options === null || options === void 0 ? void 0 : options.overwrite); + const filePath = yield this.filePath(); + const writeFunc = overwrite ? writeFile : appendFile; + yield writeFunc(filePath, this._buffer, { encoding: 'utf8' }); + return this.emptyBuffer(); + }); + } + /** + * Clears the summary buffer and wipes the summary file + * + * @returns {Summary} summary instance + */ + clear() { + return summary_awaiter(this, void 0, void 0, function* () { + return this.emptyBuffer().write({ overwrite: true }); + }); + } + /** + * Returns the current summary buffer as a string + * + * @returns {string} string of summary buffer + */ + stringify() { + return this._buffer; + } + /** + * If the summary buffer is empty + * + * @returns {boolen} true if the buffer is empty + */ + isEmptyBuffer() { + return this._buffer.length === 0; + } + /** + * Resets the summary buffer without writing to summary file + * + * @returns {Summary} summary instance + */ + emptyBuffer() { + this._buffer = ''; + return this; + } + /** + * Adds raw text to the summary buffer + * + * @param {string} text content to add + * @param {boolean} [addEOL=false] (optional) append an EOL to the raw text (default: false) + * + * @returns {Summary} summary instance + */ + addRaw(text, addEOL = false) { + this._buffer += text; + return addEOL ? this.addEOL() : this; + } + /** + * Adds the operating system-specific end-of-line marker to the buffer + * + * @returns {Summary} summary instance + */ + addEOL() { + return this.addRaw(external_os_.EOL); + } + /** + * Adds an HTML codeblock to the summary buffer + * + * @param {string} code content to render within fenced code block + * @param {string} lang (optional) language to syntax highlight code + * + * @returns {Summary} summary instance + */ + addCodeBlock(code, lang) { + const attrs = Object.assign({}, (lang && { lang })); + const element = this.wrap('pre', this.wrap('code', code), attrs); + return this.addRaw(element).addEOL(); + } + /** + * Adds an HTML list to the summary buffer + * + * @param {string[]} items list of items to render + * @param {boolean} [ordered=false] (optional) if the rendered list should be ordered or not (default: false) + * + * @returns {Summary} summary instance + */ + addList(items, ordered = false) { + const tag = ordered ? 'ol' : 'ul'; + const listItems = items.map(item => this.wrap('li', item)).join(''); + const element = this.wrap(tag, listItems); + return this.addRaw(element).addEOL(); + } + /** + * Adds an HTML table to the summary buffer + * + * @param {SummaryTableCell[]} rows table rows + * + * @returns {Summary} summary instance + */ + addTable(rows) { + const tableBody = rows + .map(row => { + const cells = row + .map(cell => { + if (typeof cell === 'string') { + return this.wrap('td', cell); + } + const { header, data, colspan, rowspan } = cell; + const tag = header ? 'th' : 'td'; + const attrs = Object.assign(Object.assign({}, (colspan && { colspan })), (rowspan && { rowspan })); + return this.wrap(tag, data, attrs); + }) + .join(''); + return this.wrap('tr', cells); + }) + .join(''); + const element = this.wrap('table', tableBody); + return this.addRaw(element).addEOL(); + } + /** + * Adds a collapsable HTML details element to the summary buffer + * + * @param {string} label text for the closed state + * @param {string} content collapsable content + * + * @returns {Summary} summary instance + */ + addDetails(label, content) { + const element = this.wrap('details', this.wrap('summary', label) + content); + return this.addRaw(element).addEOL(); + } + /** + * Adds an HTML image tag to the summary buffer + * + * @param {string} src path to the image you to embed + * @param {string} alt text description of the image + * @param {SummaryImageOptions} options (optional) addition image attributes + * + * @returns {Summary} summary instance + */ + addImage(src, alt, options) { + const { width, height } = options || {}; + const attrs = Object.assign(Object.assign({}, (width && { width })), (height && { height })); + const element = this.wrap('img', null, Object.assign({ src, alt }, attrs)); + return this.addRaw(element).addEOL(); + } + /** + * Adds an HTML section heading element + * + * @param {string} text heading text + * @param {number | string} [level=1] (optional) the heading level, default: 1 + * + * @returns {Summary} summary instance + */ + addHeading(text, level) { + const tag = `h${level}`; + const allowedTag = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(tag) + ? tag + : 'h1'; + const element = this.wrap(allowedTag, text); + return this.addRaw(element).addEOL(); + } + /** + * Adds an HTML thematic break (
) to the summary buffer + * + * @returns {Summary} summary instance + */ + addSeparator() { + const element = this.wrap('hr', null); + return this.addRaw(element).addEOL(); + } + /** + * Adds an HTML line break (
) to the summary buffer + * + * @returns {Summary} summary instance + */ + addBreak() { + const element = this.wrap('br', null); + return this.addRaw(element).addEOL(); + } + /** + * Adds an HTML blockquote to the summary buffer + * + * @param {string} text quote text + * @param {string} cite (optional) citation url + * + * @returns {Summary} summary instance + */ + addQuote(text, cite) { + const attrs = Object.assign({}, (cite && { cite })); + const element = this.wrap('blockquote', text, attrs); + return this.addRaw(element).addEOL(); + } + /** + * Adds an HTML anchor tag to the summary buffer + * + * @param {string} text link text/content + * @param {string} href hyperlink + * + * @returns {Summary} summary instance + */ + addLink(text, href) { + const element = this.wrap('a', text, { href }); + return this.addRaw(element).addEOL(); + } +} +const _summary = new Summary(); +/** + * @deprecated use `core.summary` + */ +const markdownSummary = (/* unused pure expression or super */ null && (_summary)); +const summary = (/* unused pure expression or super */ null && (_summary)); +//# sourceMappingURL=summary.js.map +;// CONCATENATED MODULE: ./node_modules/@actions/core/lib/path-utils.js + +/** + * toPosixPath converts the given path to the posix form. On Windows, \\ will be + * replaced with /. + * + * @param pth. Path to transform. + * @return string Posix path. + */ +function toPosixPath(pth) { + return pth.replace(/[\\]/g, '/'); +} +/** + * toWin32Path converts the given path to the win32 form. On Linux, / will be + * replaced with \\. + * + * @param pth. Path to transform. + * @return string Win32 path. + */ +function toWin32Path(pth) { + return pth.replace(/[/]/g, '\\'); +} +/** + * toPlatformPath converts the given path to a platform-specific path. It does + * this by replacing instances of / and \ with the platform-specific path + * separator. + * + * @param pth The path to platformize. + * @return string The platform-specific path. + */ +function toPlatformPath(pth) { + return pth.replace(/[/\\]/g, path.sep); +} +//# sourceMappingURL=path-utils.js.map +// EXTERNAL MODULE: ./node_modules/@actions/exec/lib/exec.js + 2 modules +var lib_exec = __nccwpck_require__(5260); +;// CONCATENATED MODULE: ./node_modules/@actions/core/lib/platform.js +var platform_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; + + +const getWindowsInfo = () => platform_awaiter(void 0, void 0, void 0, function* () { + const { stdout: version } = yield exec.getExecOutput('powershell -command "(Get-CimInstance -ClassName Win32_OperatingSystem).Version"', undefined, { + silent: true + }); + const { stdout: name } = yield exec.getExecOutput('powershell -command "(Get-CimInstance -ClassName Win32_OperatingSystem).Caption"', undefined, { + silent: true + }); + return { + name: name.trim(), + version: version.trim() + }; +}); +const getMacOsInfo = () => platform_awaiter(void 0, void 0, void 0, function* () { + var _a, _b, _c, _d; + const { stdout } = yield exec.getExecOutput('sw_vers', undefined, { + silent: true + }); + const version = (_b = (_a = stdout.match(/ProductVersion:\s*(.+)/)) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : ''; + const name = (_d = (_c = stdout.match(/ProductName:\s*(.+)/)) === null || _c === void 0 ? void 0 : _c[1]) !== null && _d !== void 0 ? _d : ''; + return { + name, + version + }; +}); +const getLinuxInfo = () => platform_awaiter(void 0, void 0, void 0, function* () { + const { stdout } = yield exec.getExecOutput('lsb_release', ['-i', '-r', '-s'], { + silent: true + }); + const [name, version] = stdout.trim().split('\n'); + return { + name, + version + }; +}); +const platform = external_os_.platform(); +const arch = external_os_.arch(); +const isWindows = platform === 'win32'; +const isMacOS = platform === 'darwin'; +const isLinux = platform === 'linux'; +function getDetails() { + return platform_awaiter(this, void 0, void 0, function* () { + return Object.assign(Object.assign({}, (yield (isWindows + ? getWindowsInfo() + : isMacOS + ? getMacOsInfo() + : getLinuxInfo()))), { platform, + arch, + isWindows, + isMacOS, + isLinux }); + }); +} +//# sourceMappingURL=platform.js.map +;// CONCATENATED MODULE: ./node_modules/@actions/core/lib/core.js +var core_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; + + + + + + +/** + * The code to exit an action + */ +var ExitCode; +(function (ExitCode) { + /** + * A code indicating that the action was successful + */ + ExitCode[ExitCode["Success"] = 0] = "Success"; + /** + * A code indicating that the action was a failure + */ + ExitCode[ExitCode["Failure"] = 1] = "Failure"; +})(ExitCode || (ExitCode = {})); +//----------------------------------------------------------------------- +// Variables +//----------------------------------------------------------------------- +/** + * Sets env variable for this action and future actions in the job + * @param name the name of the variable to set + * @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function exportVariable(name, val) { + const convertedVal = toCommandValue(val); + process.env[name] = convertedVal; + const filePath = process.env['GITHUB_ENV'] || ''; + if (filePath) { + return issueFileCommand('ENV', prepareKeyValueMessage(name, val)); + } + issueCommand('set-env', { name }, convertedVal); +} +/** + * Registers a secret which will get masked from logs + * + * @param secret - Value of the secret to be masked + * @remarks + * This function instructs the Actions runner to mask the specified value in any + * logs produced during the workflow run. Once registered, the secret value will + * be replaced with asterisks (***) whenever it appears in console output, logs, + * or error messages. + * + * This is useful for protecting sensitive information such as: + * - API keys + * - Access tokens + * - Authentication credentials + * - URL parameters containing signatures (SAS tokens) + * + * Note that masking only affects future logs; any previous appearances of the + * secret in logs before calling this function will remain unmasked. + * + * @example + * ```typescript + * // Register an API token as a secret + * const apiToken = "abc123xyz456"; + * setSecret(apiToken); + * + * // Now any logs containing this value will show *** instead + * console.log(`Using token: ${apiToken}`); // Outputs: "Using token: ***" + * ``` + */ +function core_setSecret(secret) { + command_issueCommand('add-mask', {}, secret); +} +/** + * Prepends inputPath to the PATH (for this action and future actions) + * @param inputPath + */ +function addPath(inputPath) { + const filePath = process.env['GITHUB_PATH'] || ''; + if (filePath) { + issueFileCommand('PATH', inputPath); + } + else { + issueCommand('add-path', {}, inputPath); + } + process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}`; +} +/** + * Gets the value of an input. + * Unless trimWhitespace is set to false in InputOptions, the value is also trimmed. + * Returns an empty string if the value is not defined. + * + * @param name name of the input to get + * @param options optional. See InputOptions. + * @returns string + */ +function getInput(name, options) { + const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || ''; + if (options && options.required && !val) { + throw new Error(`Input required and not supplied: ${name}`); + } + if (options && options.trimWhitespace === false) { + return val; + } + return val.trim(); +} +/** + * Gets the values of an multiline input. Each value is also trimmed. + * + * @param name name of the input to get + * @param options optional. See InputOptions. + * @returns string[] + * + */ +function getMultilineInput(name, options) { + const inputs = getInput(name, options) + .split('\n') + .filter(x => x !== ''); + if (options && options.trimWhitespace === false) { + return inputs; + } + return inputs.map(input => input.trim()); +} +/** + * Gets the input value of the boolean type in the YAML 1.2 "core schema" specification. + * Support boolean input list: `true | True | TRUE | false | False | FALSE` . + * The return value is also in boolean type. + * ref: https://yaml.org/spec/1.2/spec.html#id2804923 + * + * @param name name of the input to get + * @param options optional. See InputOptions. + * @returns boolean + */ +function getBooleanInput(name, options) { + const trueValue = ['true', 'True', 'TRUE']; + const falseValue = ['false', 'False', 'FALSE']; + const val = getInput(name, options); + if (trueValue.includes(val)) + return true; + if (falseValue.includes(val)) + return false; + throw new TypeError(`Input does not meet YAML 1.2 "Core Schema" specification: ${name}\n` + + `Support boolean input list: \`true | True | TRUE | false | False | FALSE\``); +} +/** + * Sets the value of an output. + * + * @param name name of the output to set + * @param value value to store. Non-string values will be converted to a string via JSON.stringify + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function setOutput(name, value) { + const filePath = process.env['GITHUB_OUTPUT'] || ''; + if (filePath) { + return issueFileCommand('OUTPUT', prepareKeyValueMessage(name, value)); + } + process.stdout.write(os.EOL); + issueCommand('set-output', { name }, toCommandValue(value)); +} +/** + * Enables or disables the echoing of commands into stdout for the rest of the step. + * Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set. + * + */ +function setCommandEcho(enabled) { + issue('echo', enabled ? 'on' : 'off'); +} +//----------------------------------------------------------------------- +// Results +//----------------------------------------------------------------------- +/** + * Sets the action status to failed. + * When the action exits it will be with an exit code of 1 + * @param message add error issue message + */ +function setFailed(message) { + process.exitCode = ExitCode.Failure; + error(message); +} +//----------------------------------------------------------------------- +// Logging Commands +//----------------------------------------------------------------------- +/** + * Gets whether Actions Step Debug is on or not + */ +function isDebug() { + return process.env['RUNNER_DEBUG'] === '1'; +} +/** + * Writes debug message to user log + * @param message debug message + */ +function core_debug(message) { + command_issueCommand('debug', {}, message); +} +/** + * Adds an error issue + * @param message error issue message. Errors will be converted to string via toString() + * @param properties optional properties to add to the annotation. + */ +function error(message, properties = {}) { + command_issueCommand('error', utils_toCommandProperties(properties), message instanceof Error ? message.toString() : message); +} +/** + * Adds a warning issue + * @param message warning issue message. Errors will be converted to string via toString() + * @param properties optional properties to add to the annotation. + */ +function warning(message, properties = {}) { + command_issueCommand('warning', utils_toCommandProperties(properties), message instanceof Error ? message.toString() : message); +} +/** + * Adds a notice issue + * @param message notice issue message. Errors will be converted to string via toString() + * @param properties optional properties to add to the annotation. + */ +function notice(message, properties = {}) { + issueCommand('notice', toCommandProperties(properties), message instanceof Error ? message.toString() : message); +} +/** + * Writes info to log with console.log. + * @param message info message + */ +function info(message) { + process.stdout.write(message + external_os_.EOL); +} +/** + * Begin an output group. + * + * Output until the next `groupEnd` will be foldable in this group + * + * @param name The name of the output group + */ +function startGroup(name) { + issue('group', name); +} +/** + * End an output group. + */ +function endGroup() { + issue('endgroup'); +} +/** + * Wrap an asynchronous function call in a group. + * + * Returns the same type as the function itself. + * + * @param name The name of the group + * @param fn The function to wrap in the group + */ +function group(name, fn) { + return core_awaiter(this, void 0, void 0, function* () { + startGroup(name); + let result; + try { + result = yield fn(); + } + finally { + endGroup(); + } + return result; + }); +} +//----------------------------------------------------------------------- +// Wrapper action state +//----------------------------------------------------------------------- +/** + * Saves state for current action, the state can only be retrieved by this action's post job execution. + * + * @param name name of the state to store + * @param value value to store. Non-string values will be converted to a string via JSON.stringify + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function saveState(name, value) { + const filePath = process.env['GITHUB_STATE'] || ''; + if (filePath) { + return issueFileCommand('STATE', prepareKeyValueMessage(name, value)); + } + issueCommand('save-state', { name }, toCommandValue(value)); +} +/** + * Gets the value of an state set by this action's main execution. + * + * @param name name of the state to get + * @returns string + */ +function getState(name) { + return process.env[`STATE_${name}`] || ''; +} +function getIDToken(aud) { + return core_awaiter(this, void 0, void 0, function* () { + return yield OidcClient.getIDToken(aud); + }); +} +/** + * Summary exports + */ + +/** + * @deprecated use core.summary + */ + +/** + * Path exports + */ + +/** + * Platform utilities exports + */ + +//# sourceMappingURL=core.js.map + +/***/ }), + +/***/ 5260: +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __nccwpck_require__) => { + + +// EXPORTS +__nccwpck_require__.d(__webpack_exports__, { + m: () => (/* binding */ exec) +}); + +// UNUSED EXPORTS: getExecOutput + +// EXTERNAL MODULE: external "string_decoder" +var external_string_decoder_ = __nccwpck_require__(3193); +// EXTERNAL MODULE: external "os" +var external_os_ = __nccwpck_require__(857); +// EXTERNAL MODULE: external "events" +var external_events_ = __nccwpck_require__(4434); +// EXTERNAL MODULE: external "child_process" +var external_child_process_ = __nccwpck_require__(5317); +// EXTERNAL MODULE: external "path" +var external_path_ = __nccwpck_require__(6928); +// EXTERNAL MODULE: ./node_modules/@actions/io/lib/io.js +var io = __nccwpck_require__(8701); +// EXTERNAL MODULE: ./node_modules/@actions/io/lib/io-util.js +var io_util = __nccwpck_require__(90); +;// CONCATENATED MODULE: external "timers" +const external_timers_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("timers"); +;// CONCATENATED MODULE: ./node_modules/@actions/exec/lib/toolrunner.js +var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; + + + + + + + +/* eslint-disable @typescript-eslint/unbound-method */ +const IS_WINDOWS = process.platform === 'win32'; +/* + * Class for running command line tools. Handles quoting and arg parsing in a platform agnostic way. + */ +class ToolRunner extends external_events_.EventEmitter { + constructor(toolPath, args, options) { + super(); + if (!toolPath) { + throw new Error("Parameter 'toolPath' cannot be null or empty."); + } + this.toolPath = toolPath; + this.args = args || []; + this.options = options || {}; + } + _debug(message) { + if (this.options.listeners && this.options.listeners.debug) { + this.options.listeners.debug(message); + } + } + _getCommandString(options, noPrefix) { + const toolPath = this._getSpawnFileName(); + const args = this._getSpawnArgs(options); + let cmd = noPrefix ? '' : '[command]'; // omit prefix when piped to a second tool + if (IS_WINDOWS) { + // Windows + cmd file + if (this._isCmdFile()) { + cmd += toolPath; + for (const a of args) { + cmd += ` ${a}`; + } + } + // Windows + verbatim + else if (options.windowsVerbatimArguments) { + cmd += `"${toolPath}"`; + for (const a of args) { + cmd += ` ${a}`; + } + } + // Windows (regular) + else { + cmd += this._windowsQuoteCmdArg(toolPath); + for (const a of args) { + cmd += ` ${this._windowsQuoteCmdArg(a)}`; + } + } + } + else { + // OSX/Linux - this can likely be improved with some form of quoting. + // creating processes on Unix is fundamentally different than Windows. + // on Unix, execvp() takes an arg array. + cmd += toolPath; + for (const a of args) { + cmd += ` ${a}`; + } + } + return cmd; + } + _processLineBuffer(data, strBuffer, onLine) { + try { + let s = strBuffer + data.toString(); + let n = s.indexOf(external_os_.EOL); + while (n > -1) { + const line = s.substring(0, n); + onLine(line); + // the rest of the string ... + s = s.substring(n + external_os_.EOL.length); + n = s.indexOf(external_os_.EOL); + } + return s; + } + catch (err) { + // streaming lines to console is best effort. Don't fail a build. + this._debug(`error processing line. Failed with error ${err}`); + return ''; + } + } + _getSpawnFileName() { + if (IS_WINDOWS) { + if (this._isCmdFile()) { + return process.env['COMSPEC'] || 'cmd.exe'; + } + } + return this.toolPath; + } + _getSpawnArgs(options) { + if (IS_WINDOWS) { + if (this._isCmdFile()) { + let argline = `/D /S /C "${this._windowsQuoteCmdArg(this.toolPath)}`; + for (const a of this.args) { + argline += ' '; + argline += options.windowsVerbatimArguments + ? a + : this._windowsQuoteCmdArg(a); + } + argline += '"'; + return [argline]; + } + } + return this.args; + } + _endsWith(str, end) { + return str.endsWith(end); + } + _isCmdFile() { + const upperToolPath = this.toolPath.toUpperCase(); + return (this._endsWith(upperToolPath, '.CMD') || + this._endsWith(upperToolPath, '.BAT')); + } + _windowsQuoteCmdArg(arg) { + // for .exe, apply the normal quoting rules that libuv applies + if (!this._isCmdFile()) { + return this._uvQuoteCmdArg(arg); + } + // otherwise apply quoting rules specific to the cmd.exe command line parser. + // the libuv rules are generic and are not designed specifically for cmd.exe + // command line parser. + // + // for a detailed description of the cmd.exe command line parser, refer to + // http://stackoverflow.com/questions/4094699/how-does-the-windows-command-interpreter-cmd-exe-parse-scripts/7970912#7970912 + // need quotes for empty arg + if (!arg) { + return '""'; + } + // determine whether the arg needs to be quoted + const cmdSpecialChars = [ + ' ', + '\t', + '&', + '(', + ')', + '[', + ']', + '{', + '}', + '^', + '=', + ';', + '!', + "'", + '+', + ',', + '`', + '~', + '|', + '<', + '>', + '"' + ]; + let needsQuotes = false; + for (const char of arg) { + if (cmdSpecialChars.some(x => x === char)) { + needsQuotes = true; + break; + } + } + // short-circuit if quotes not needed + if (!needsQuotes) { + return arg; + } + // the following quoting rules are very similar to the rules that by libuv applies. + // + // 1) wrap the string in quotes + // + // 2) double-up quotes - i.e. " => "" + // + // this is different from the libuv quoting rules. libuv replaces " with \", which unfortunately + // doesn't work well with a cmd.exe command line. + // + // note, replacing " with "" also works well if the arg is passed to a downstream .NET console app. + // for example, the command line: + // foo.exe "myarg:""my val""" + // is parsed by a .NET console app into an arg array: + // [ "myarg:\"my val\"" ] + // which is the same end result when applying libuv quoting rules. although the actual + // command line from libuv quoting rules would look like: + // foo.exe "myarg:\"my val\"" + // + // 3) double-up slashes that precede a quote, + // e.g. hello \world => "hello \world" + // hello\"world => "hello\\""world" + // hello\\"world => "hello\\\\""world" + // hello world\ => "hello world\\" + // + // technically this is not required for a cmd.exe command line, or the batch argument parser. + // the reasons for including this as a .cmd quoting rule are: + // + // a) this is optimized for the scenario where the argument is passed from the .cmd file to an + // external program. many programs (e.g. .NET console apps) rely on the slash-doubling rule. + // + // b) it's what we've been doing previously (by deferring to node default behavior) and we + // haven't heard any complaints about that aspect. + // + // note, a weakness of the quoting rules chosen here, is that % is not escaped. in fact, % cannot be + // escaped when used on the command line directly - even though within a .cmd file % can be escaped + // by using %%. + // + // the saving grace is, on the command line, %var% is left as-is if var is not defined. this contrasts + // the line parsing rules within a .cmd file, where if var is not defined it is replaced with nothing. + // + // one option that was explored was replacing % with ^% - i.e. %var% => ^%var^%. this hack would + // often work, since it is unlikely that var^ would exist, and the ^ character is removed when the + // variable is used. the problem, however, is that ^ is not removed when %* is used to pass the args + // to an external program. + // + // an unexplored potential solution for the % escaping problem, is to create a wrapper .cmd file. + // % can be escaped within a .cmd file. + let reverse = '"'; + let quoteHit = true; + for (let i = arg.length; i > 0; i--) { + // walk the string in reverse + reverse += arg[i - 1]; + if (quoteHit && arg[i - 1] === '\\') { + reverse += '\\'; // double the slash } else if (arg[i - 1] === '"') { quoteHit = true; @@ -34010,329 +34411,127 @@ function findInPath(tool) { if (_io_util_js__WEBPACK_IMPORTED_MODULE_2__/* .IS_WINDOWS */ .H8 && process.env['PATHEXT']) { for (const extension of process.env['PATHEXT'].split(path__WEBPACK_IMPORTED_MODULE_1__.delimiter)) { if (extension) { - extensions.push(extension); - } - } - } - // if it's rooted, return it if exists. otherwise return empty. - if (_io_util_js__WEBPACK_IMPORTED_MODULE_2__/* .isRooted */ .Qh(tool)) { - const filePath = yield _io_util_js__WEBPACK_IMPORTED_MODULE_2__/* .tryGetExecutablePath */ .vr(tool, extensions); - if (filePath) { - return [filePath]; - } - return []; - } - // if any path separators, return empty - if (tool.includes(path__WEBPACK_IMPORTED_MODULE_1__.sep)) { - return []; - } - // build the list of directories - // - // Note, technically "where" checks the current directory on Windows. From a toolkit perspective, - // it feels like we should not do this. Checking the current directory seems like more of a use - // case of a shell, and the which() function exposed by the toolkit should strive for consistency - // across platforms. - const directories = []; - if (process.env.PATH) { - for (const p of process.env.PATH.split(path__WEBPACK_IMPORTED_MODULE_1__.delimiter)) { - if (p) { - directories.push(p); - } - } - } - // find all matches - const matches = []; - for (const directory of directories) { - const filePath = yield _io_util_js__WEBPACK_IMPORTED_MODULE_2__/* .tryGetExecutablePath */ .vr(path__WEBPACK_IMPORTED_MODULE_1__.join(directory, tool), extensions); - if (filePath) { - matches.push(filePath); - } - } - return matches; - }); -} -function readCopyOptions(options) { - const force = options.force == null ? true : options.force; - const recursive = Boolean(options.recursive); - const copySourceDirectory = options.copySourceDirectory == null - ? true - : Boolean(options.copySourceDirectory); - return { force, recursive, copySourceDirectory }; -} -function cpDirRecursive(sourceDir, destDir, currentDepth, force) { - return __awaiter(this, void 0, void 0, function* () { - // Ensure there is not a run away recursive copy - if (currentDepth >= 255) - return; - currentDepth++; - yield mkdirP(destDir); - const files = yield ioUtil.readdir(sourceDir); - for (const fileName of files) { - const srcFile = `${sourceDir}/${fileName}`; - const destFile = `${destDir}/${fileName}`; - const srcFileStat = yield ioUtil.lstat(srcFile); - if (srcFileStat.isDirectory()) { - // Recurse - yield cpDirRecursive(srcFile, destFile, currentDepth, force); - } - else { - yield copyFile(srcFile, destFile, force); + extensions.push(extension); + } } } - // Change the mode for the newly created directory - yield ioUtil.chmod(destDir, (yield ioUtil.stat(sourceDir)).mode); - }); -} -// Buffered file copy -function copyFile(srcFile, destFile, force) { - return __awaiter(this, void 0, void 0, function* () { - if ((yield ioUtil.lstat(srcFile)).isSymbolicLink()) { - // unlink/re-link it - try { - yield ioUtil.lstat(destFile); - yield ioUtil.unlink(destFile); + // if it's rooted, return it if exists. otherwise return empty. + if (_io_util_js__WEBPACK_IMPORTED_MODULE_2__/* .isRooted */ .Qh(tool)) { + const filePath = yield _io_util_js__WEBPACK_IMPORTED_MODULE_2__/* .tryGetExecutablePath */ .vr(tool, extensions); + if (filePath) { + return [filePath]; } - catch (e) { - // Try to override file permission - if (e.code === 'EPERM') { - yield ioUtil.chmod(destFile, '0666'); - yield ioUtil.unlink(destFile); + return []; + } + // if any path separators, return empty + if (tool.includes(path__WEBPACK_IMPORTED_MODULE_1__.sep)) { + return []; + } + // build the list of directories + // + // Note, technically "where" checks the current directory on Windows. From a toolkit perspective, + // it feels like we should not do this. Checking the current directory seems like more of a use + // case of a shell, and the which() function exposed by the toolkit should strive for consistency + // across platforms. + const directories = []; + if (process.env.PATH) { + for (const p of process.env.PATH.split(path__WEBPACK_IMPORTED_MODULE_1__.delimiter)) { + if (p) { + directories.push(p); } - // other errors = it doesn't exist, no work to do } - // Copy over symlink - const symlinkFull = yield ioUtil.readlink(srcFile); - yield ioUtil.symlink(symlinkFull, destFile, ioUtil.IS_WINDOWS ? 'junction' : null); - } - else if (!(yield ioUtil.exists(destFile)) || force) { - yield ioUtil.copyFile(srcFile, destFile); } - }); -} -//# 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__ = {}; + // find all matches + const matches = []; + for (const directory of directories) { + const filePath = yield _io_util_js__WEBPACK_IMPORTED_MODULE_2__/* .tryGetExecutablePath */ .vr(path__WEBPACK_IMPORTED_MODULE_1__.join(directory, tool), extensions); + if (filePath) { + matches.push(filePath); + } + } + return matches; + }); +} +function readCopyOptions(options) { + const force = options.force == null ? true : options.force; + const recursive = Boolean(options.recursive); + const copySourceDirectory = options.copySourceDirectory == null + ? true + : Boolean(options.copySourceDirectory); + return { force, recursive, copySourceDirectory }; +} +function cpDirRecursive(sourceDir, destDir, currentDepth, force) { + return __awaiter(this, void 0, void 0, function* () { + // Ensure there is not a run away recursive copy + if (currentDepth >= 255) + return; + currentDepth++; + yield mkdirP(destDir); + const files = yield ioUtil.readdir(sourceDir); + for (const fileName of files) { + const srcFile = `${sourceDir}/${fileName}`; + const destFile = `${destDir}/${fileName}`; + const srcFileStat = yield ioUtil.lstat(srcFile); + if (srcFileStat.isDirectory()) { + // Recurse + yield cpDirRecursive(srcFile, destFile, currentDepth, force); + } + else { + yield copyFile(srcFile, destFile, force); + } + } + // Change the mode for the newly created directory + yield ioUtil.chmod(destDir, (yield ioUtil.stat(sourceDir)).mode); + }); +} +// Buffered file copy +function copyFile(srcFile, destFile, force) { + return __awaiter(this, void 0, void 0, function* () { + if ((yield ioUtil.lstat(srcFile)).isSymbolicLink()) { + // unlink/re-link it + try { + yield ioUtil.lstat(destFile); + yield ioUtil.unlink(destFile); + } + catch (e) { + // Try to override file permission + if (e.code === 'EPERM') { + yield ioUtil.chmod(destFile, '0666'); + yield ioUtil.unlink(destFile); + } + // other errors = it doesn't exist, no work to do + } + // Copy over symlink + const symlinkFull = yield ioUtil.readlink(srcFile); + yield ioUtil.symlink(symlinkFull, destFile, ioUtil.IS_WINDOWS ? 'junction' : null); + } + else if (!(yield ioUtil.exists(destFile)) || force) { + yield ioUtil.copyFile(srcFile, destFile); + } + }); +} +//# sourceMappingURL=io.js.map + +/***/ }), + +/***/ 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); }); } @@ -34638,211 +34841,43 @@ function downloadToolAttempt(url, dest, auth, headers) { * interface, it is smaller than the full command line interface, and it does support long paths. At the * time of this writing, it is freely available from the LZMA SDK that is available on the 7zip website. * Be sure to check the current license agreement. If 7zr.exe is bundled with your action, then the path - * to 7zr.exe can be pass to this function. - * @returns path to the destination directory - */ -function extract7z(file, dest, _7zPath) { - return tool_cache_awaiter(this, void 0, void 0, function* () { - ok(IS_WINDOWS, 'extract7z() not supported on current OS'); - ok(file, 'parameter "file" is required'); - dest = yield _createExtractFolder(dest); - const originalCwd = process.cwd(); - process.chdir(dest); - if (_7zPath) { - try { - const logLevel = core.isDebug() ? '-bb1' : '-bb0'; - const args = [ - 'x', // eXtract files with full paths - logLevel, // -bb[0-3] : set output log level - '-bd', // disable progress indicator - '-sccUTF-8', // set charset for for console input/output - file - ]; - const options = { - silent: true - }; - yield exec(`"${_7zPath}"`, args, options); - } - finally { - process.chdir(originalCwd); - } - } - else { - const escapedScript = path - .join(__dirname, '..', 'scripts', 'Invoke-7zdec.ps1') - .replace(/'/g, "''") - .replace(/"|\n|\r/g, ''); // double-up single quotes, remove double quotes and newlines - const escapedFile = file.replace(/'/g, "''").replace(/"|\n|\r/g, ''); - const escapedTarget = dest.replace(/'/g, "''").replace(/"|\n|\r/g, ''); - const command = `& '${escapedScript}' -Source '${escapedFile}' -Target '${escapedTarget}'`; - const args = [ - '-NoLogo', - '-Sta', - '-NoProfile', - '-NonInteractive', - '-ExecutionPolicy', - 'Unrestricted', - '-Command', - command - ]; - const options = { - silent: true - }; - try { - const powershellPath = yield io.which('powershell', true); - yield exec(`"${powershellPath}"`, args, options); - } - finally { - process.chdir(originalCwd); - } - } - return dest; - }); -} -/** - * Extract a compressed tar archive - * - * @param file path to the tar - * @param dest destination directory. Optional. - * @param flags flags for the tar command to use for extraction. Defaults to 'xz' (extracting gzipped tars). Optional. - * @returns path to the destination directory - */ -function extractTar(file_1, dest_1) { - return tool_cache_awaiter(this, arguments, void 0, function* (file, dest, flags = 'xz') { - if (!file) { - throw new Error("parameter 'file' is required"); - } - // Create dest - dest = yield _createExtractFolder(dest); - // Determine whether GNU tar - core.debug('Checking tar --version'); - let versionOutput = ''; - yield exec('tar --version', [], { - ignoreReturnCode: true, - silent: true, - listeners: { - stdout: (data) => (versionOutput += data.toString()), - stderr: (data) => (versionOutput += data.toString()) - } - }); - core.debug(versionOutput.trim()); - const isGnuTar = versionOutput.toUpperCase().includes('GNU TAR'); - // Initialize args - let args; - if (flags instanceof Array) { - args = flags; - } - else { - args = [flags]; - } - if (core.isDebug() && !flags.includes('v')) { - args.push('-v'); - } - let destArg = dest; - let fileArg = file; - if (IS_WINDOWS && isGnuTar) { - args.push('--force-local'); - destArg = dest.replace(/\\/g, '/'); - // Technically only the dest needs to have `/` but for aesthetic consistency - // convert slashes in the file arg too. - fileArg = file.replace(/\\/g, '/'); - } - if (isGnuTar) { - // Suppress warnings when using GNU tar to extract archives created by BSD tar - args.push('--warning=no-unknown-keyword'); - args.push('--overwrite'); - } - args.push('-C', destArg, '-f', fileArg); - yield exec(`tar`, args); - return dest; - }); -} -/** - * Extract a xar compatible archive - * - * @param file path to the archive - * @param dest destination directory. Optional. - * @param flags flags for the xar. Optional. - * @returns path to the destination directory - */ -function extractXar(file_1, dest_1) { - return tool_cache_awaiter(this, arguments, void 0, function* (file, dest, flags = []) { - ok(IS_MAC, 'extractXar() not supported on current OS'); - ok(file, 'parameter "file" is required'); - dest = yield _createExtractFolder(dest); - let args; - if (flags instanceof Array) { - args = flags; - } - else { - args = [flags]; - } - args.push('-x', '-C', dest, '-f', file); - if (core.isDebug()) { - args.push('-v'); - } - const xarPath = yield io.which('xar', true); - yield exec(`"${xarPath}"`, _unique(args)); - return dest; - }); -} -/** - * Extract a zip - * - * @param file path to the zip - * @param dest destination directory. Optional. - * @returns path to the destination directory - */ -function extractZip(file, dest) { - return tool_cache_awaiter(this, void 0, void 0, function* () { - if (!file) { - throw new Error("parameter 'file' is required"); - } - dest = yield _createExtractFolder(dest); - if (IS_WINDOWS) { - yield extractZipWin(file, dest); - } - else { - yield extractZipNix(file, dest); - } - return dest; - }); -} -function extractZipWin(file, dest) { + * to 7zr.exe can be pass to this function. + * @returns path to the destination directory + */ +function extract7z(file, dest, _7zPath) { return tool_cache_awaiter(this, void 0, void 0, function* () { - // build the powershell command - const escapedFile = file.replace(/'/g, "''").replace(/"|\n|\r/g, ''); // double-up single quotes, remove double quotes and newlines - const escapedDest = dest.replace(/'/g, "''").replace(/"|\n|\r/g, ''); - const pwshPath = yield io.which('pwsh', false); - //To match the file overwrite behavior on nix systems, we use the overwrite = true flag for ExtractToDirectory - //and the -Force flag for Expand-Archive as a fallback - if (pwshPath) { - //attempt to use pwsh with ExtractToDirectory, if this fails attempt Expand-Archive - const pwshCommand = [ - `$ErrorActionPreference = 'Stop' ;`, - `try { Add-Type -AssemblyName System.IO.Compression.ZipFile } catch { } ;`, - `try { [System.IO.Compression.ZipFile]::ExtractToDirectory('${escapedFile}', '${escapedDest}', $true) }`, - `catch { if (($_.Exception.GetType().FullName -eq 'System.Management.Automation.MethodException') -or ($_.Exception.GetType().FullName -eq 'System.Management.Automation.RuntimeException') ){ Expand-Archive -LiteralPath '${escapedFile}' -DestinationPath '${escapedDest}' -Force } else { throw $_ } } ;` - ].join(' '); - const args = [ - '-NoLogo', - '-NoProfile', - '-NonInteractive', - '-ExecutionPolicy', - 'Unrestricted', - '-Command', - pwshCommand - ]; - core.debug(`Using pwsh at path: ${pwshPath}`); - yield exec(`"${pwshPath}"`, args); + ok(IS_WINDOWS, 'extract7z() not supported on current OS'); + ok(file, 'parameter "file" is required'); + dest = yield _createExtractFolder(dest); + const originalCwd = process.cwd(); + process.chdir(dest); + if (_7zPath) { + try { + const logLevel = core.isDebug() ? '-bb1' : '-bb0'; + const args = [ + 'x', // eXtract files with full paths + logLevel, // -bb[0-3] : set output log level + '-bd', // disable progress indicator + '-sccUTF-8', // set charset for for console input/output + file + ]; + const options = { + silent: true + }; + yield exec(`"${_7zPath}"`, args, options); + } + finally { + process.chdir(originalCwd); + } } else { - const powershellCommand = [ - `$ErrorActionPreference = 'Stop' ;`, - `try { Add-Type -AssemblyName System.IO.Compression.FileSystem } catch { } ;`, - `if ((Get-Command -Name Expand-Archive -Module Microsoft.PowerShell.Archive -ErrorAction Ignore)) { Expand-Archive -LiteralPath '${escapedFile}' -DestinationPath '${escapedDest}' -Force }`, - `else {[System.IO.Compression.ZipFile]::ExtractToDirectory('${escapedFile}', '${escapedDest}', $true) }` - ].join(' '); + const escapedScript = path + .join(__dirname, '..', 'scripts', 'Invoke-7zdec.ps1') + .replace(/'/g, "''") + .replace(/"|\n|\r/g, ''); // double-up single quotes, remove double quotes and newlines + const escapedFile = file.replace(/'/g, "''").replace(/"|\n|\r/g, ''); + const escapedTarget = dest.replace(/'/g, "''").replace(/"|\n|\r/g, ''); + const command = `& '${escapedScript}' -Source '${escapedFile}' -Target '${escapedTarget}'`; const args = [ '-NoLogo', '-Sta', @@ -34851,650 +34886,680 @@ function extractZipWin(file, dest) { '-ExecutionPolicy', 'Unrestricted', '-Command', - powershellCommand + command ]; - const powershellPath = yield io.which('powershell', true); - core.debug(`Using powershell at path: ${powershellPath}`); - yield exec(`"${powershellPath}"`, args); - } - }); -} -function extractZipNix(file, dest) { - return tool_cache_awaiter(this, void 0, void 0, function* () { - const unzipPath = yield io.which('unzip', true); - const args = [file]; - if (!core.isDebug()) { - args.unshift('-q'); - } - args.unshift('-o'); //overwrite with -o, otherwise a prompt is shown which freezes the run - yield exec(`"${unzipPath}"`, args, { cwd: dest }); - }); -} -/** - * Caches a directory and installs it into the tool cacheDir - * - * @param sourceDir the directory to cache into tools - * @param tool tool name - * @param version version of the tool. semver format - * @param arch architecture of the tool. Optional. Defaults to machine architecture - */ -function cacheDir(sourceDir, tool, version, arch) { - return tool_cache_awaiter(this, void 0, void 0, function* () { - version = semver.clean(version) || version; - arch = arch || os.arch(); - core.debug(`Caching tool ${tool} ${version} ${arch}`); - core.debug(`source dir: ${sourceDir}`); - if (!fs.statSync(sourceDir).isDirectory()) { - throw new Error('sourceDir is not a directory'); - } - // Create the tool dir - const destPath = yield _createToolPath(tool, version, arch); - // copy each child item. do not move. move can fail on Windows - // due to anti-virus software having an open handle on a file. - for (const itemName of fs.readdirSync(sourceDir)) { - const s = path.join(sourceDir, itemName); - yield io.cp(s, destPath, { recursive: true }); - } - // write .complete - _completeToolPath(tool, version, arch); - return destPath; - }); -} -/** - * Caches a downloaded file (GUID) and installs it - * into the tool cache with a given targetName - * - * @param sourceFile the file to cache into tools. Typically a result of downloadTool which is a guid. - * @param targetFile the name of the file name in the tools directory - * @param tool tool name - * @param version version of the tool. semver format - * @param arch architecture of the tool. Optional. Defaults to machine architecture - */ -function cacheFile(sourceFile, targetFile, tool, version, arch) { - return tool_cache_awaiter(this, void 0, void 0, function* () { - version = semver.clean(version) || version; - arch = arch || os.arch(); - core.debug(`Caching tool ${tool} ${version} ${arch}`); - core.debug(`source file: ${sourceFile}`); - if (!fs.statSync(sourceFile).isFile()) { - throw new Error('sourceFile is not a file'); + const options = { + silent: true + }; + try { + const powershellPath = yield io.which('powershell', true); + yield exec(`"${powershellPath}"`, args, options); + } + finally { + process.chdir(originalCwd); + } } - // create the tool dir - const destFolder = yield _createToolPath(tool, version, arch); - // copy instead of move. move can fail on Windows due to - // anti-virus software having an open handle on a file. - const destPath = path.join(destFolder, targetFile); - core.debug(`destination file ${destPath}`); - yield io.cp(sourceFile, destPath); - // write .complete - _completeToolPath(tool, version, arch); - return destFolder; + return dest; }); } /** - * Finds the path to a tool version in the local installed tool cache - * - * @param toolName name of the tool - * @param versionSpec version of the tool - * @param arch optional arch. defaults to arch of computer - */ -function find(toolName, versionSpec, arch) { - if (!toolName) { - throw new Error('toolName parameter is required'); - } - if (!versionSpec) { - throw new Error('versionSpec parameter is required'); - } - arch = arch || os.arch(); - // attempt to resolve an explicit version - if (!isExplicitVersion(versionSpec)) { - const localVersions = findAllVersions(toolName, arch); - const match = evaluateVersions(localVersions, versionSpec); - versionSpec = match; - } - // check for the explicit version in the cache - let toolPath = ''; - if (versionSpec) { - versionSpec = semver.clean(versionSpec) || ''; - const cachePath = path.join(_getCacheDirectory(), toolName, versionSpec, arch); - core.debug(`checking cache: ${cachePath}`); - if (fs.existsSync(cachePath) && fs.existsSync(`${cachePath}.complete`)) { - core.debug(`Found tool in cache ${toolName} ${versionSpec} ${arch}`); - toolPath = cachePath; - } - else { - core.debug('not found'); - } - } - return toolPath; -} -/** - * Finds the paths to all versions of a tool that are installed in the local tool cache + * Extract a compressed tar archive * - * @param toolName name of the tool - * @param arch optional arch. defaults to arch of computer + * @param file path to the tar + * @param dest destination directory. Optional. + * @param flags flags for the tar command to use for extraction. Defaults to 'xz' (extracting gzipped tars). Optional. + * @returns path to the destination directory */ -function findAllVersions(toolName, arch) { - const versions = []; - arch = arch || os.arch(); - const toolPath = path.join(_getCacheDirectory(), toolName); - if (fs.existsSync(toolPath)) { - const children = fs.readdirSync(toolPath); - for (const child of children) { - if (isExplicitVersion(child)) { - const fullPath = path.join(toolPath, child, arch || ''); - if (fs.existsSync(fullPath) && fs.existsSync(`${fullPath}.complete`)) { - versions.push(child); - } - } - } - } - return versions; -} -function getManifestFromRepo(owner_1, repo_1, auth_1) { - return tool_cache_awaiter(this, arguments, void 0, function* (owner, repo, auth, branch = 'master') { - let releases = []; - const treeUrl = `https://api.github.com/repos/${owner}/${repo}/git/trees/${branch}`; - const http = new httpm.HttpClient('tool-cache'); - const headers = {}; - if (auth) { - core.debug('set auth'); - headers.authorization = auth; - } - const response = yield http.getJson(treeUrl, headers); - if (!response.result) { - return releases; - } - let manifestUrl = ''; - for (const item of response.result.tree) { - if (item.path === 'versions-manifest.json') { - manifestUrl = item.url; - break; - } +function extractTar(file_1, dest_1) { + return tool_cache_awaiter(this, arguments, void 0, function* (file, dest, flags = 'xz') { + if (!file) { + throw new Error("parameter 'file' is required"); } - headers['accept'] = 'application/vnd.github.VERSION.raw'; - let versionsRaw = yield (yield http.get(manifestUrl, headers)).readBody(); - if (versionsRaw) { - // shouldn't be needed but protects against invalid json saved with BOM - versionsRaw = versionsRaw.replace(/^\uFEFF/, ''); - try { - releases = JSON.parse(versionsRaw); - } - catch (_a) { - core.debug('Invalid json'); + // Create dest + dest = yield _createExtractFolder(dest); + // Determine whether GNU tar + core.debug('Checking tar --version'); + let versionOutput = ''; + yield exec('tar --version', [], { + ignoreReturnCode: true, + silent: true, + listeners: { + stdout: (data) => (versionOutput += data.toString()), + stderr: (data) => (versionOutput += data.toString()) } + }); + core.debug(versionOutput.trim()); + const isGnuTar = versionOutput.toUpperCase().includes('GNU TAR'); + // Initialize args + let args; + if (flags instanceof Array) { + args = flags; } - return releases; - }); -} -function findFromManifest(versionSpec_1, stable_1, manifest_1) { - return tool_cache_awaiter(this, arguments, void 0, function* (versionSpec, stable, manifest, archFilter = os.arch()) { - // wrap the internal impl - const match = yield mm._findMatch(versionSpec, stable, manifest, archFilter); - return match; - }); -} -function _createExtractFolder(dest) { - return tool_cache_awaiter(this, void 0, void 0, function* () { - if (!dest) { - // create a temp dir - dest = path.join(_getTempDirectory(), crypto.randomUUID()); + else { + args = [flags]; } - yield io.mkdirP(dest); + if (core.isDebug() && !flags.includes('v')) { + args.push('-v'); + } + let destArg = dest; + let fileArg = file; + if (IS_WINDOWS && isGnuTar) { + args.push('--force-local'); + destArg = dest.replace(/\\/g, '/'); + // Technically only the dest needs to have `/` but for aesthetic consistency + // convert slashes in the file arg too. + fileArg = file.replace(/\\/g, '/'); + } + if (isGnuTar) { + // Suppress warnings when using GNU tar to extract archives created by BSD tar + args.push('--warning=no-unknown-keyword'); + args.push('--overwrite'); + } + args.push('-C', destArg, '-f', fileArg); + yield exec(`tar`, args); return dest; }); } -function _createToolPath(tool, version, arch) { - return tool_cache_awaiter(this, void 0, void 0, function* () { - const folderPath = path.join(_getCacheDirectory(), tool, semver.clean(version) || version, arch || ''); - core.debug(`destination ${folderPath}`); - const markerPath = `${folderPath}.complete`; - yield io.rmRF(folderPath); - yield io.rmRF(markerPath); - yield io.mkdirP(folderPath); - return folderPath; - }); -} -function _completeToolPath(tool, version, arch) { - const folderPath = path.join(_getCacheDirectory(), tool, semver.clean(version) || version, arch || ''); - const markerPath = `${folderPath}.complete`; - fs.writeFileSync(markerPath, ''); - core.debug('finished caching tool'); -} /** - * Check if version string is explicit + * Extract a xar compatible archive * - * @param versionSpec version string to check + * @param file path to the archive + * @param dest destination directory. Optional. + * @param flags flags for the xar. Optional. + * @returns path to the destination directory */ -function isExplicitVersion(versionSpec) { - const c = semver.clean(versionSpec) || ''; - core.debug(`isExplicit: ${c}`); - const valid = semver.valid(c) != null; - core.debug(`explicit? ${valid}`); - return valid; +function extractXar(file_1, dest_1) { + return tool_cache_awaiter(this, arguments, void 0, function* (file, dest, flags = []) { + ok(IS_MAC, 'extractXar() not supported on current OS'); + ok(file, 'parameter "file" is required'); + dest = yield _createExtractFolder(dest); + let args; + if (flags instanceof Array) { + args = flags; + } + else { + args = [flags]; + } + args.push('-x', '-C', dest, '-f', file); + if (core.isDebug()) { + args.push('-v'); + } + const xarPath = yield io.which('xar', true); + yield exec(`"${xarPath}"`, _unique(args)); + return dest; + }); } /** - * Get the highest satisfiying semantic version in `versions` which satisfies `versionSpec` + * Extract a zip * - * @param versions array of versions to evaluate - * @param versionSpec semantic version spec to satisfy + * @param file path to the zip + * @param dest destination directory. Optional. + * @returns path to the destination directory */ -function evaluateVersions(versions, versionSpec) { - let version = ''; - core.debug(`evaluating ${versions.length} versions`); - versions = versions.sort((a, b) => { - if (semver.gt(a, b)) { - return 1; +function extractZip(file, dest) { + return tool_cache_awaiter(this, void 0, void 0, function* () { + if (!file) { + throw new Error("parameter 'file' is required"); } - return -1; - }); - for (let i = versions.length - 1; i >= 0; i--) { - const potential = versions[i]; - const satisfied = semver.satisfies(potential, versionSpec); - if (satisfied) { - version = potential; - break; + dest = yield _createExtractFolder(dest); + if (IS_WINDOWS) { + yield extractZipWin(file, dest); } - } - if (version) { - core.debug(`matched: ${version}`); - } - else { - core.debug('match not found'); - } - return version; -} -/** - * Gets RUNNER_TOOL_CACHE - */ -function _getCacheDirectory() { - const cacheDirectory = process.env['RUNNER_TOOL_CACHE'] || ''; - ok(cacheDirectory, 'Expected RUNNER_TOOL_CACHE to be defined'); - return cacheDirectory; -} -/** - * Gets RUNNER_TEMP - */ -function _getTempDirectory() { - const tempDirectory = process.env['RUNNER_TEMP'] || ''; - ok(tempDirectory, 'Expected RUNNER_TEMP to be defined'); - return tempDirectory; -} -/** - * Gets a global variable - */ -function _getGlobal(key, defaultValue) { - /* eslint-disable @typescript-eslint/no-explicit-any */ - const value = global[key]; - /* eslint-enable @typescript-eslint/no-explicit-any */ - return value !== undefined ? value : defaultValue; -} -/** - * Returns an array of unique values. - * @param values Values to make unique. - */ -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 - - - - - - - -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); + else { + yield extractZipNix(file, dest); } - } - 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'; + return dest; + }); } -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; +function extractZipWin(file, dest) { + return tool_cache_awaiter(this, void 0, void 0, function* () { + // build the powershell command + const escapedFile = file.replace(/'/g, "''").replace(/"|\n|\r/g, ''); // double-up single quotes, remove double quotes and newlines + const escapedDest = dest.replace(/'/g, "''").replace(/"|\n|\r/g, ''); + const pwshPath = yield io.which('pwsh', false); + //To match the file overwrite behavior on nix systems, we use the overwrite = true flag for ExtractToDirectory + //and the -Force flag for Expand-Archive as a fallback + if (pwshPath) { + //attempt to use pwsh with ExtractToDirectory, if this fails attempt Expand-Archive + const pwshCommand = [ + `$ErrorActionPreference = 'Stop' ;`, + `try { Add-Type -AssemblyName System.IO.Compression.ZipFile } catch { } ;`, + `try { [System.IO.Compression.ZipFile]::ExtractToDirectory('${escapedFile}', '${escapedDest}', $true) }`, + `catch { if (($_.Exception.GetType().FullName -eq 'System.Management.Automation.MethodException') -or ($_.Exception.GetType().FullName -eq 'System.Management.Automation.RuntimeException') ){ Expand-Archive -LiteralPath '${escapedFile}' -DestinationPath '${escapedDest}' -Force } else { throw $_ } } ;` + ].join(' '); + const args = [ + '-NoLogo', + '-NoProfile', + '-NonInteractive', + '-ExecutionPolicy', + 'Unrestricted', + '-Command', + pwshCommand + ]; + core.debug(`Using pwsh at path: ${pwshPath}`); + yield exec(`"${pwshPath}"`, args); } - } - 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; + else { + const powershellCommand = [ + `$ErrorActionPreference = 'Stop' ;`, + `try { Add-Type -AssemblyName System.IO.Compression.FileSystem } catch { } ;`, + `if ((Get-Command -Name Expand-Archive -Module Microsoft.PowerShell.Archive -ErrorAction Ignore)) { Expand-Archive -LiteralPath '${escapedFile}' -DestinationPath '${escapedDest}' -Force }`, + `else {[System.IO.Compression.ZipFile]::ExtractToDirectory('${escapedFile}', '${escapedDest}', $true) }` + ].join(' '); + const args = [ + '-NoLogo', + '-Sta', + '-NoProfile', + '-NonInteractive', + '-ExecutionPolicy', + 'Unrestricted', + '-Command', + powershellCommand + ]; + const powershellPath = yield io.which('powershell', true); + core.debug(`Using powershell at path: ${powershellPath}`); + yield exec(`"${powershellPath}"`, args); + } + }); } -function isJobStatusSuccess() { - const jobStatus = lib_core/* getInput */.V4(INPUT_JOB_STATUS); - return jobStatus === 'success'; +function extractZipNix(file, dest) { + return tool_cache_awaiter(this, void 0, void 0, function* () { + const unzipPath = yield io.which('unzip', true); + const args = [file]; + if (!core.isDebug()) { + args.unshift('-q'); + } + args.unshift('-o'); //overwrite with -o, otherwise a prompt is shown which freezes the run + yield exec(`"${unzipPath}"`, args, { cwd: dest }); + }); } -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; +/** + * Caches a directory and installs it into the tool cacheDir + * + * @param sourceDir the directory to cache into tools + * @param tool tool name + * @param version version of the tool. semver format + * @param arch architecture of the tool. Optional. Defaults to machine architecture + */ +function cacheDir(sourceDir, tool, version, arch) { + return tool_cache_awaiter(this, void 0, void 0, function* () { + version = semver.clean(version) || version; + arch = arch || os.arch(); + core.debug(`Caching tool ${tool} ${version} ${arch}`); + core.debug(`source dir: ${sourceDir}`); + if (!fs.statSync(sourceDir).isDirectory()) { + throw new Error('sourceDir is not a directory'); + } + // Create the tool dir + const destPath = yield _createToolPath(tool, version, arch); + // copy each child item. do not move. move can fail on Windows + // due to anti-virus software having an open handle on a file. + for (const itemName of fs.readdirSync(sourceDir)) { + const s = path.join(sourceDir, itemName); + yield io.cp(s, destPath, { recursive: true }); + } + // write .complete + _completeToolPath(tool, version, arch); + return destPath; + }); } -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}'`); +/** + * Caches a downloaded file (GUID) and installs it + * into the tool cache with a given targetName + * + * @param sourceFile the file to cache into tools. Typically a result of downloadTool which is a guid. + * @param targetFile the name of the file name in the tools directory + * @param tool tool name + * @param version version of the tool. semver format + * @param arch architecture of the tool. Optional. Defaults to machine architecture + */ +function cacheFile(sourceFile, targetFile, tool, version, arch) { + return tool_cache_awaiter(this, void 0, void 0, function* () { + version = semver.clean(version) || version; + arch = arch || os.arch(); + core.debug(`Caching tool ${tool} ${version} ${arch}`); + core.debug(`source file: ${sourceFile}`); + if (!fs.statSync(sourceFile).isFile()) { + throw new Error('sourceFile is not a file'); } - } - 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 - }; + // create the tool dir + const destFolder = yield _createToolPath(tool, version, arch); + // copy instead of move. move can fail on Windows due to + // anti-virus software having an open handle on a file. + const destPath = path.join(destFolder, targetFile); + core.debug(`destination file ${destPath}`); + yield io.cp(sourceFile, destPath); + // write .complete + _completeToolPath(tool, version, arch); + return destFolder; + }); } -// 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.`); +/** + * Finds the path to a tool version in the local installed tool cache + * + * @param toolName name of the tool + * @param versionSpec version of the tool + * @param arch optional arch. defaults to arch of computer + */ +function find(toolName, versionSpec, arch) { + if (!toolName) { + throw new Error('toolName parameter is required'); } - 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 (!versionSpec) { + throw new Error('versionSpec parameter is required'); } - if (normalized.startsWith('oracle-graalvm')) { - return 'graalvm'; + arch = arch || os.arch(); + // attempt to resolve an explicit version + if (!isExplicitVersion(versionSpec)) { + const localVersions = findAllVersions(toolName, arch); + const match = evaluateVersions(localVersions, versionSpec); + versionSpec = match; } - 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.`); + // check for the explicit version in the cache + let toolPath = ''; + if (versionSpec) { + versionSpec = semver.clean(versionSpec) || ''; + const cachePath = path.join(_getCacheDirectory(), toolName, versionSpec, arch); + core.debug(`checking cache: ${cachePath}`); + if (fs.existsSync(cachePath) && fs.existsSync(`${cachePath}.complete`)) { + core.debug(`Found tool in cache ${toolName} ${versionSpec} ${arch}`); + toolPath = cachePath; + } + else { + core.debug('not found'); + } } - 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; + return toolPath; } -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('.')}`; +/** + * Finds the paths to all versions of a tool that are installed in the local tool cache + * + * @param toolName name of the tool + * @param arch optional arch. defaults to arch of computer + */ +function findAllVersions(toolName, arch) { + const versions = []; + arch = arch || os.arch(); + const toolPath = path.join(_getCacheDirectory(), toolName); + if (fs.existsSync(toolPath)) { + const children = fs.readdirSync(toolPath); + for (const child of children) { + if (isExplicitVersion(child)) { + const fullPath = path.join(toolPath, child, arch || ''); + if (fs.existsSync(fullPath) && fs.existsSync(`${fullPath}.complete`)) { + versions.push(child); + } + } + } } - return mainVersion; + return versions; } -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; +function getManifestFromRepo(owner_1, repo_1, auth_1) { + return tool_cache_awaiter(this, arguments, void 0, function* (owner, repo, auth, branch = 'master') { + let releases = []; + const treeUrl = `https://api.github.com/repos/${owner}/${repo}/git/trees/${branch}`; + const http = new httpm.HttpClient('tool-cache'); + const headers = {}; + if (auth) { + core.debug('set auth'); + headers.authorization = auth; + } + const response = yield http.getJson(treeUrl, headers); + if (!response.result) { + return releases; + } + let manifestUrl = ''; + for (const item of response.result.tree) { + if (item.path === 'versions-manifest.json') { + manifestUrl = item.url; + break; + } + } + headers['accept'] = 'application/vnd.github.VERSION.raw'; + let versionsRaw = yield (yield http.get(manifestUrl, headers)).readBody(); + if (versionsRaw) { + // shouldn't be needed but protects against invalid json saved with BOM + versionsRaw = versionsRaw.replace(/^\uFEFF/, ''); + try { + releases = JSON.parse(versionsRaw); + } + catch (_a) { + core.debug('Invalid json'); + } + } + return releases; + }); } -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]; +function findFromManifest(versionSpec_1, stable_1, manifest_1) { + return tool_cache_awaiter(this, arguments, void 0, function* (versionSpec, stable, manifest, archFilter = os.arch()) { + // wrap the internal impl + const match = yield mm._findMatch(versionSpec, stable, manifest, archFilter); + return match; + }); +} +function _createExtractFolder(dest) { + return tool_cache_awaiter(this, void 0, void 0, function* () { + if (!dest) { + // create a temp dir + dest = path.join(_getTempDirectory(), crypto.randomUUID()); } - } - return null; + yield io.mkdirP(dest); + return dest; + }); } -function validatePaginationUrl(url, allowedOrigin) { - try { - const parsed = new URL(url); - const allowed = new URL(allowedOrigin); - return parsed.origin === allowed.origin; +function _createToolPath(tool, version, arch) { + return tool_cache_awaiter(this, void 0, void 0, function* () { + const folderPath = path.join(_getCacheDirectory(), tool, semver.clean(version) || version, arch || ''); + core.debug(`destination ${folderPath}`); + const markerPath = `${folderPath}.complete`; + yield io.rmRF(folderPath); + yield io.rmRF(markerPath); + yield io.mkdirP(folderPath); + return folderPath; + }); +} +function _completeToolPath(tool, version, arch) { + const folderPath = path.join(_getCacheDirectory(), tool, semver.clean(version) || version, arch || ''); + const markerPath = `${folderPath}.complete`; + fs.writeFileSync(markerPath, ''); + core.debug('finished caching tool'); +} +/** + * Check if version string is explicit + * + * @param versionSpec version string to check + */ +function isExplicitVersion(versionSpec) { + const c = semver.clean(versionSpec) || ''; + core.debug(`isExplicit: ${c}`); + const valid = semver.valid(c) != null; + core.debug(`explicit? ${valid}`); + return valid; +} +/** + * Get the highest satisfiying semantic version in `versions` which satisfies `versionSpec` + * + * @param versions array of versions to evaluate + * @param versionSpec semantic version spec to satisfy + */ +function evaluateVersions(versions, versionSpec) { + let version = ''; + core.debug(`evaluating ${versions.length} versions`); + versions = versions.sort((a, b) => { + if (semver.gt(a, b)) { + return 1; + } + return -1; + }); + for (let i = versions.length - 1; i >= 0; i--) { + const potential = versions[i]; + const satisfied = semver.satisfies(potential, versionSpec); + if (satisfied) { + version = potential; + break; + } } - catch { - return false; + if (version) { + core.debug(`matched: ${version}`); + } + else { + core.debug('match not found'); } + return version; +} +/** + * Gets RUNNER_TOOL_CACHE + */ +function _getCacheDirectory() { + const cacheDirectory = process.env['RUNNER_TOOL_CACHE'] || ''; + ok(cacheDirectory, 'Expected RUNNER_TOOL_CACHE to be defined'); + return cacheDirectory; +} +/** + * Gets RUNNER_TEMP + */ +function _getTempDirectory() { + const tempDirectory = process.env['RUNNER_TEMP'] || ''; + ok(tempDirectory, 'Expected RUNNER_TEMP to be defined'); + return tempDirectory; } -// 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; +/** + * Gets a global variable + */ +function _getGlobal(key, defaultValue) { + /* eslint-disable @typescript-eslint/no-explicit-any */ + const value = global[key]; + /* eslint-enable @typescript-eslint/no-explicit-any */ + return value !== undefined ? value : defaultValue; } -// 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); +/** + * Returns an array of unique values. + * @param values Values to make unique. + */ +function _unique(values) { + return Array.from(new Set(values)); } +//# sourceMappingURL=tool-cache.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__ = {}; +// EXPORTS +__nccwpck_require__.d(__webpack_exports__, { + e: () => (/* binding */ run) +}); + +// 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 5b3a5918b..77843a5cd 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 2564989e1..75d8153a9 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 000000000..ff832e637 --- /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 d73a0c052..f83b7ac85 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 9fe143f04..7854db9df 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 138ca3a4b..2e4f42beb 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 e1474c230..73ea57c89 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 3716098e7..b1962e635 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 e95bae185..6e19a5873 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 d771b9271..34d6c7cfe 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 2bec34f41..44706fdae 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 840c19dcb..f9c492c66 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 000000000..afee92060 --- /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 e998f7b64..c361c5ff0 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 3706ab9df..6bcd0d170 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));