diff --git a/.github/workflows/e2e-cache.yml b/.github/workflows/e2e-cache.yml index e4fd46c1..9f6835d7 100644 --- a/.github/workflows/e2e-cache.yml +++ b/.github/workflows/e2e-cache.yml @@ -21,7 +21,7 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-13, windows-latest, ubuntu-latest] + os: [macos-15-intel, windows-latest, ubuntu-latest] steps: - name: Checkout uses: actions/checkout@v5 @@ -46,7 +46,7 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-13, windows-latest, ubuntu-latest] + os: [macos-15-intel, windows-latest, ubuntu-latest] needs: gradle-save steps: - name: Checkout @@ -70,7 +70,7 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-13, windows-latest, ubuntu-latest] + os: [macos-15-intel, windows-latest, ubuntu-latest] steps: - name: Checkout uses: actions/checkout@v5 @@ -93,7 +93,7 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-13, windows-latest, ubuntu-latest] + os: [macos-15-intel, windows-latest, ubuntu-latest] needs: maven-save steps: - name: Checkout @@ -121,7 +121,7 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-13, windows-latest, ubuntu-22.04] + os: [macos-15-intel, windows-latest, ubuntu-22.04] steps: - name: Checkout uses: actions/checkout@v5 @@ -133,7 +133,7 @@ jobs: java-version: '11' cache: sbt - name: Setup SBT - if: matrix.os == 'macos-13' + if: matrix.os == 'macos-15-intel' run: | echo ""Installing SBT..."" brew install sbt @@ -141,7 +141,7 @@ jobs: run: sbt update - name: Check files to cache on macos-latest - if: matrix.os == 'macos-13' + if: matrix.os == 'macos-15-intel' run: | if [ ! -d ~/Library/Caches/Coursier ]; then echo "::error::The ~/Library/Caches/Coursier directory does not exist unexpectedly" @@ -170,7 +170,7 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-13, windows-latest, ubuntu-22.04] + os: [macos-15-intel, windows-latest, ubuntu-22.04] needs: sbt-save steps: - name: Checkout @@ -184,7 +184,7 @@ jobs: cache: sbt - name: Confirm that ~/Library/Caches/Coursier directory has been made - if: matrix.os == 'macos-13' + if: matrix.os == 'macos-15-intel' run: | if [ ! -d ~/Library/Caches/Coursier ]; then echo "::error::The ~/Library/Caches/Coursier directory does not exist unexpectedly" diff --git a/.github/workflows/e2e-versions.yml b/.github/workflows/e2e-versions.yml index 5dde96b0..afe9fe4c 100644 --- a/.github/workflows/e2e-versions.yml +++ b/.github/workflows/e2e-versions.yml @@ -20,7 +20,7 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-13, windows-latest, ubuntu-latest] + os: [macos-15-intel, windows-latest, ubuntu-latest] distribution: [ 'temurin', 'adopt', @@ -39,7 +39,7 @@ jobs: - distribution: microsoft version: 8 - distribution: dragonwell - os: macos-13 + os: macos-15-intel include: - distribution: microsoft os: windows-latest @@ -51,7 +51,7 @@ jobs: os: macos-latest version: 25 - distribution: oracle - os: macos-13 + os: macos-15-intel version: 17 - distribution: oracle os: windows-latest @@ -229,7 +229,7 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-13, windows-latest, ubuntu-latest] + os: [macos-15-intel, windows-latest, ubuntu-latest] version: ['17-ea', '15.0.0-ea.14'] steps: - name: Checkout @@ -295,7 +295,7 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-13, windows-latest, ubuntu-latest] + os: [macos-15-intel, windows-latest, ubuntu-latest] distribution: ['temurin', 'zulu', 'liberica', 'semeru', 'sapmachine', 'jetbrains'] java-package: ['jre'] diff --git a/dist/setup/index.js b/dist/setup/index.js index c4e8ec9e..7d2a97c1 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -129856,6 +129856,7 @@ class JavaBase { this.checkLatest = installerOptions.checkLatest; } setupJava() { + var _a, _b; return __awaiter(this, void 0, void 0, function* () { let foundJava = this.findInToolcache(); if (foundJava && !this.checkLatest) { @@ -129863,40 +129864,106 @@ class JavaBase { } else { core.info('Trying to resolve the latest version from remote'); - try { - const javaRelease = yield this.findPackageForDownload(this.version); - core.info(`Resolved latest version as ${javaRelease.version}`); - if ((foundJava === null || foundJava === void 0 ? void 0 : foundJava.version) === javaRelease.version) { - core.info(`Resolved Java ${foundJava.version} from tool-cache`); - } - else { - core.info('Trying to download...'); - foundJava = yield this.downloadTool(javaRelease); - core.info(`Java ${foundJava.version} was downloaded`); - } - } - catch (error) { - if (error instanceof tc.HTTPError) { - if (error.httpStatusCode === 403) { - core.error('HTTP 403: Permission denied or access restricted.'); + const MAX_RETRIES = 4; + const RETRY_DELAY_MS = 2000; + const retryableCodes = [ + 'ETIMEDOUT', + 'ECONNRESET', + 'ENOTFOUND', + 'ECONNREFUSED' + ]; + let retries = MAX_RETRIES; + while (retries > 0) { + try { + // Clear console timers before each attempt to prevent conflicts + if (retries < MAX_RETRIES && core.isDebug()) { + const consoleAny = console; + (_b = (_a = consoleAny._times) === null || _a === void 0 ? void 0 : _a.clear) === null || _b === void 0 ? void 0 : _b.call(_a); } - else if (error.httpStatusCode === 429) { - core.warning('HTTP 429: Rate limit exceeded. Please retry later.'); + const javaRelease = yield this.findPackageForDownload(this.version); + core.info(`Resolved latest version as ${javaRelease.version}`); + if ((foundJava === null || foundJava === void 0 ? void 0 : foundJava.version) === javaRelease.version) { + core.info(`Resolved Java ${foundJava.version} from tool-cache`); } else { - core.error(`HTTP ${error.httpStatusCode}: ${error.message}`); + core.info('Trying to download...'); + foundJava = yield this.downloadTool(javaRelease); + core.info(`Java ${foundJava.version} was downloaded`); } + break; } - else { - const message = error instanceof Error ? error.message : JSON.stringify(error); - core.error(`Java setup failed due to network issue or timeout: ${message}`); + catch (error) { + retries--; + // Check if error is retryable (including aggregate errors) + const isRetryable = (error instanceof tc.HTTPError && + error.httpStatusCode && + [429, 502, 503, 504].includes(error.httpStatusCode)) || + retryableCodes.includes(error === null || error === void 0 ? void 0 : error.code) || + ((error === null || error === void 0 ? void 0 : error.errors) && + Array.isArray(error.errors) && + error.errors.some((err) => retryableCodes.includes(err === null || err === void 0 ? void 0 : err.code))); + if (retries > 0 && isRetryable) { + core.debug(`Attempt failed due to network or timeout issues, initiating retry... (${retries} attempts left)`); + yield new Promise(r => setTimeout(r, RETRY_DELAY_MS)); + continue; + } + if (error instanceof tc.HTTPError) { + if (error.httpStatusCode === 403) { + core.error('HTTP 403: Permission denied or access restricted.'); + } + else if (error.httpStatusCode === 429) { + core.warning('HTTP 429: Rate limit exceeded. Please retry later.'); + } + else { + core.error(`HTTP ${error.httpStatusCode}: ${error.message}`); + } + } + else if (error && error.errors && Array.isArray(error.errors)) { + core.error(`Java setup failed due to network or configuration error(s)`); + if (error instanceof Error && error.stack) { + core.debug(error.stack); + } + for (const err of error.errors) { + const endpoint = (err === null || err === void 0 ? void 0 : err.address) || (err === null || err === void 0 ? void 0 : err.hostname) || ''; + const port = (err === null || err === void 0 ? void 0 : err.port) ? `:${err.port}` : ''; + const message = (err === null || err === void 0 ? void 0 : err.message) || 'Aggregate error'; + const endpointInfo = !message.includes(endpoint) + ? ` ${endpoint}${port}` + : ''; + const localInfo = err.localAddress && err.localPort + ? ` - Local (${err.localAddress}:${err.localPort})` + : ''; + const logMessage = `${message}${endpointInfo}${localInfo}`; + core.error(logMessage); + core.debug(`${err.stack || err.message}`); + Object.entries(err).forEach(([key, value]) => { + core.debug(`"${key}": ${JSON.stringify(value)}`); + }); + } + } + else { + const message = error instanceof Error ? error.message : JSON.stringify(error); + core.error(`Java setup process failed due to: ${message}`); + if (typeof (error === null || error === void 0 ? void 0 : error.code) === 'string') { + core.debug(error.stack); + } + const errorDetails = Object.assign({ name: error.name, message: error.message }, Object.getOwnPropertyNames(error) + .filter(prop => !['name', 'message', 'stack'].includes(prop)) + .reduce((acc, prop) => { + acc[prop] = error[prop]; + return acc; + }, {})); + Object.entries(errorDetails).forEach(([key, value]) => { + core.debug(`"${key}": ${JSON.stringify(value)}`); + }); + } + throw error; } - if (error instanceof Error && error.stack) { - core.debug(error.stack); - } - throw error; } } + if (!foundJava) { + throw new Error('Failed to resolve Java version'); + } // JDK folder may contain postfix "Contents/Home" on macOS const macOSPostfixPath = path_1.default.join(foundJava.path, constants_1.MACOS_JAVA_CONTENT_POSTFIX); if (process.platform === 'darwin' && fs.existsSync(macOSPostfixPath)) { diff --git a/package-lock.json b/package-lock.json index c99ed7e9..cf750144 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,4 +1,3 @@ - { "name": "setup-java", "version": "5.0.0", diff --git a/src/distributions/base-installer.ts b/src/distributions/base-installer.ts index 4a83897f..03b4759d 100644 --- a/src/distributions/base-installer.ts +++ b/src/distributions/base-installer.ts @@ -51,39 +51,115 @@ export abstract class JavaBase { core.info(`Resolved Java ${foundJava.version} from tool-cache`); } else { core.info('Trying to resolve the latest version from remote'); - try { - const javaRelease = await this.findPackageForDownload(this.version); - core.info(`Resolved latest version as ${javaRelease.version}`); - if (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`); - } - } catch (error: any) { - if (error instanceof tc.HTTPError) { - if (error.httpStatusCode === 403) { - core.error('HTTP 403: Permission denied or access restricted.'); - } else if (error.httpStatusCode === 429) { - core.warning('HTTP 429: Rate limit exceeded. Please retry later.'); - } else { - core.error(`HTTP ${error.httpStatusCode}: ${error.message}`); + const MAX_RETRIES = 4; + const RETRY_DELAY_MS = 2000; + const retryableCodes = [ + 'ETIMEDOUT', + 'ECONNRESET', + 'ENOTFOUND', + 'ECONNREFUSED' + ]; + let retries = MAX_RETRIES; + while (retries > 0) { + try { + // Clear console timers before each attempt to prevent conflicts + if (retries < MAX_RETRIES && core.isDebug()) { + const consoleAny = console as any; + consoleAny._times?.clear?.(); } - } else { - const message = - error instanceof Error ? error.message : JSON.stringify(error); - core.error( - `Java setup failed due to network issue or timeout: ${message}` - ); + const javaRelease = await this.findPackageForDownload(this.version); + core.info(`Resolved latest version as ${javaRelease.version}`); + if (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`); + } + break; + } catch (error: any) { + retries--; + // Check if error is retryable (including aggregate errors) + const isRetryable = + (error instanceof tc.HTTPError && + error.httpStatusCode && + [429, 502, 503, 504].includes(error.httpStatusCode)) || + retryableCodes.includes(error?.code) || + (error?.errors && + Array.isArray(error.errors) && + error.errors.some((err: any) => + retryableCodes.includes(err?.code) + )); + if (retries > 0 && isRetryable) { + core.debug( + `Attempt failed due to network or timeout issues, initiating retry... (${retries} attempts left)` + ); + await new Promise(r => setTimeout(r, RETRY_DELAY_MS)); + continue; + } + if (error instanceof tc.HTTPError) { + if (error.httpStatusCode === 403) { + core.error('HTTP 403: Permission denied or access restricted.'); + } else if (error.httpStatusCode === 429) { + core.warning( + 'HTTP 429: Rate limit exceeded. Please retry later.' + ); + } else { + core.error(`HTTP ${error.httpStatusCode}: ${error.message}`); + } + } else if (error && error.errors && Array.isArray(error.errors)) { + core.error( + `Java setup failed due to network or configuration error(s)` + ); + if (error instanceof Error && error.stack) { + core.debug(error.stack); + } + for (const err of error.errors) { + const endpoint = err?.address || err?.hostname || ''; + const port = err?.port ? `:${err.port}` : ''; + const message = err?.message || 'Aggregate error'; + const endpointInfo = !message.includes(endpoint) + ? ` ${endpoint}${port}` + : ''; + const localInfo = + err.localAddress && err.localPort + ? ` - Local (${err.localAddress}:${err.localPort})` + : ''; + const logMessage = `${message}${endpointInfo}${localInfo}`; + core.error(logMessage); + core.debug(`${err.stack || err.message}`); + Object.entries(err).forEach(([key, value]) => { + core.debug(`"${key}": ${JSON.stringify(value)}`); + }); + } + } else { + const message = + error instanceof Error ? error.message : JSON.stringify(error); + core.error(`Java setup process failed due to: ${message}`); + if (typeof error?.code === 'string') { + core.debug(error.stack); + } + const errorDetails = { + name: error.name, + message: error.message, + ...Object.getOwnPropertyNames(error) + .filter(prop => !['name', 'message', 'stack'].includes(prop)) + .reduce<{[key: string]: any}>((acc, prop) => { + acc[prop] = error[prop]; + return acc; + }, {}) + }; + Object.entries(errorDetails).forEach(([key, value]) => { + core.debug(`"${key}": ${JSON.stringify(value)}`); + }); + } + throw error; } - if (error instanceof Error && error.stack) { - core.debug(error.stack); - } - throw error; } } - + if (!foundJava) { + throw new Error('Failed to resolve Java version'); + } // JDK folder may contain postfix "Contents/Home" on macOS const macOSPostfixPath = path.join( foundJava.path,