mirror of
https://github.com/actions/setup-java.git
synced 2026-11-14 07:52:24 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d1374f8ac7 | |||
| 5fb63c9bc9 | |||
| a1bf0b5568 | |||
| a3c8068301 | |||
| 9ce5b12cb9 | |||
| 77dd5b2ce2 | |||
| 644a850ee5 | |||
| dade0c5eef | |||
| ef0b922255 | |||
| e144521edf | |||
| 8cd57eb681 | |||
| 816722acf8 | |||
| b843ebf9b9 | |||
| 2a69be27b0 | |||
| d0e6e4dbf7 | |||
| b24925bc49 | |||
| 47b36480ae | |||
| 74920aab4a | |||
| a78ec39f12 | |||
| 97c5a5e13a | |||
| 1aa87b638d |
@@ -0,0 +1,43 @@
|
||||
# Copilot instructions
|
||||
|
||||
## Build, test, and lint
|
||||
|
||||
Use Node.js 24 or later (`package.json` declares `>=24.0.0`).
|
||||
|
||||
- `npm ci` installs the locked dependencies.
|
||||
- `npm test` runs Jest serially with coverage.
|
||||
- `npm test -- --runTestsByPath __tests__/setup-java.test.ts` runs one test file.
|
||||
- `npm test -- --runTestsByPath __tests__/setup-java.test.ts --testNamePattern="pattern"` filters that file to matching test names.
|
||||
- `npm run lint` runs ESLint on TypeScript files.
|
||||
- `npm run format-check` checks Prettier formatting; `npm run format` applies it.
|
||||
- `npm run build` bundles the setup and post-job entrypoints into `dist/`.
|
||||
- `npm run check` runs format check, lint, build, and the full test suite.
|
||||
|
||||
## Architecture
|
||||
|
||||
This repository implements a Node.js GitHub Action. `action.yml` defines its inputs and outputs and runs `dist/setup/index.js` as the main entrypoint and `dist/cleanup/index.js` as the post-job entrypoint. The TypeScript sources in `src/` are bundled with NCC; the committed `dist/` files are what the action executes.
|
||||
|
||||
The setup entrypoint parses inputs, resolves versions, and configures the action's shared features. `src/distributions/distribution-factory.ts` lazily selects a vendor installer. Vendor installers implement metadata lookup and archive handling on top of `JavaBase` in `src/distributions/base-installer.ts`, which centralizes version resolution, tool-cache/JDK-cache use, download verification, and Java environment setup. Package support and OS/architecture support are validated centrally in `package-types.ts` and `platform-types.ts`.
|
||||
|
||||
The setup flow also configures problem matchers, Maven authentication and arguments, and Maven toolchains. The post-job entrypoint removes any isolated GPG home and saves enabled caches when the job succeeds and cache saving is not read-only.
|
||||
|
||||
## Repository-specific conventions
|
||||
|
||||
- Keep TypeScript source imports ESM-compatible: relative imports include the `.js` extension, even when the source file is `.ts`.
|
||||
- Distribution-specific download and metadata logic belongs in its installer under `src/distributions/`; reuse `JavaBase` rather than duplicating common resolution, caching, verification, or environment behavior.
|
||||
- When adding or changing distribution support, keep the distribution enum, factory, package-capability map, platform-capability map, README support documentation, unit tests, and relevant end-to-end workflow coverage consistent.
|
||||
- Unit tests live under `__tests__/` and use Jest/ts-jest. Distribution tests are grouped under `__tests__/distributors/`; use the corresponding test and fixture patterns when changing an installer. End-to-end coverage is defined in `.github/workflows/e2e-*.yml`.
|
||||
- Rebuild and commit generated `dist/` bundles for runtime-affecting source or dependency changes; do not make the bundles the source of truth. `npm run check` includes the build.
|
||||
- New npm dependencies need lockfile changes and corresponding license records under `.licenses/`, following `.licensed.yml` and the licensed workflow.
|
||||
|
||||
## Action behavior principles
|
||||
|
||||
- Treat `action.yml` inputs, outputs, defaults, and compatibility aliases as the public API. Keep implementation, README, and advanced-usage documentation aligned when behavior changes; preserve deprecated aliases unless a documented breaking change removes them.
|
||||
- Do not assume version syntax, package variants, or platform/architecture support is uniform across distributions. Keep capability checks and vendor resolution behavior consistent with the documented support matrix. `latest` means the newest stable GA release, not an early-access build.
|
||||
- Preserve selection semantics: a recognized vendor in a supported version file may supply the distribution; otherwise the caller must provide it. If both `java-version` and `java-version-file` are given, the explicit `java-version` takes precedence.
|
||||
- Preserve install-state behavior for multiple JDKs. The last installed JDK becomes the default unless `set-default: false`; all installed JDKs remain available through their version-specific environment variables and Maven toolchains. Toolchain entries accumulate non-destructively, and custom IDs must correspond one-for-one with requested versions.
|
||||
- Treat dependency, wrapper, and JDK caches as distinct features with distinct keys and save behavior. `cache` enables dependency caching and implicitly enables JDK caching unless `cache-jdk` overrides it; `cache-read-only` restores without saving. Keep changes to cache identity or lifecycle aligned with these documented contracts.
|
||||
- Preserve download-integrity guarantees: verify authoritative checksums when published. Signature verification is distribution-specific; explicit `verify-signature: true` must enforce verification where supported and fail clearly where unsupported. Do not silently weaken verification or imply that a cached install was downloaded and reverified.
|
||||
- Keep Maven credential inputs as environment-variable names, not secret values. Generated settings and custom repository behavior must respect the documented overwrite option, while toolchain merging remains non-destructive and independent of that option.
|
||||
|
||||
See `docs/CONTRIBUTING.md` for contribution and validation guidance, `README.md` for the public contract, and `docs/advanced-usage.md` for detailed behavior and examples.
|
||||
@@ -174,3 +174,203 @@ jobs:
|
||||
if (-not (Test-Path $path)) {
|
||||
throw "settings.xml file is not found in expected location"
|
||||
}
|
||||
|
||||
test-maven-mirrors:
|
||||
name: Maven mirrors (${{ matrix.mirror-of }})
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest, ubuntu-latest]
|
||||
mirror-of: ['*', 'external:*']
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Configure Maven mirrors without repository inputs
|
||||
uses: $/
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '21'
|
||||
settings-path: ${{ runner.temp }}/maven-mirror-test
|
||||
mvn-server-credentials: |
|
||||
private:PRIVATE_USERNAME:PRIVATE_TOKEN
|
||||
mvn-mirrors: |
|
||||
private:https://mirror.example.invalid/maven:${{ matrix.mirror-of }}
|
||||
- name: Validate generated settings and Maven mirror selection
|
||||
env:
|
||||
MIRROR_OF: ${{ matrix.mirror-of }}
|
||||
PRIVATE_USERNAME: mirror-test-user
|
||||
PRIVATE_TOKEN: mirror-test-token
|
||||
run: |
|
||||
$directory = Join-Path $env:RUNNER_TEMP "maven-mirror-test"
|
||||
$settingsPath = Join-Path $directory "settings.xml"
|
||||
[xml]$settings = Get-Content -Path $settingsPath -Raw
|
||||
$mirrors = @($settings.settings.mirrors.mirror)
|
||||
if ($mirrors.Count -ne 1 -or $mirrors[0].id -ne "private" -or $mirrors[0].url -ne "https://mirror.example.invalid/maven" -or $mirrors[0].mirrorOf -ne $env:MIRROR_OF) {
|
||||
throw "Incorrect generated mirror"
|
||||
}
|
||||
if ($settings.settings.profiles -or $settings.settings.activeProfiles) {
|
||||
throw "Mirror-only settings must not add repository profiles"
|
||||
}
|
||||
$server = $settings.settings.servers.server
|
||||
if ($server.id -ne "private" -or $server.username -ne '${env.PRIVATE_USERNAME}' -or $server.password -ne '${env.PRIVATE_TOKEN}') {
|
||||
throw "Mirror credentials were not preserved"
|
||||
}
|
||||
|
||||
$pomPath = Join-Path $directory "pom.xml"
|
||||
@'
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>setup-java.test</groupId>
|
||||
<artifactId>mirror-settings</artifactId>
|
||||
<version>1.0</version>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>pom-dependencies</id>
|
||||
<url>https://dependencies.example.invalid/maven</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>pom-plugins</id>
|
||||
<url>https://plugins.example.invalid/maven</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
</project>
|
||||
'@ | Set-Content -Path $pomPath
|
||||
|
||||
# validate requires no plugins or dependencies, so mirror selection can
|
||||
# be inspected offline without contacting the deliberately invalid URLs.
|
||||
$output = & mvn --batch-mode --no-transfer-progress --offline --debug `
|
||||
--settings $settingsPath --file $pomPath `
|
||||
"-Dmaven.repo.local=$directory/repository" validate 2>&1
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Could not inspect Maven mirror selection: $output"
|
||||
}
|
||||
$log = $output -join "`n"
|
||||
foreach ($id in @("central", "pom-dependencies")) {
|
||||
$expected = "Using mirror private (https://mirror.example.invalid/maven) for $id ("
|
||||
if (-not $log.Contains($expected)) {
|
||||
throw "Maven did not select the mirror for ${id}: $log"
|
||||
}
|
||||
}
|
||||
foreach ($type in @("dependencies", "plugins")) {
|
||||
$pattern = 'Repositories \(' + $type + '\)\s*: \[private \(https://mirror\.example\.invalid/maven, [^\]]+\)\]'
|
||||
if ($log -notmatch $pattern) {
|
||||
throw "Maven did not redirect all $type repositories: $log"
|
||||
}
|
||||
}
|
||||
|
||||
test-maven-repositories:
|
||||
name: Maven ${{ matrix.repositories }} repositories
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest, ubuntu-latest]
|
||||
repositories: [dependencies, plugins, both]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Prepare Maven model inspection
|
||||
run: |
|
||||
$directory = Join-Path $env:RUNNER_TEMP "maven-repository-test"
|
||||
New-Item -Path $directory -ItemType Directory -Force | Out-Null
|
||||
$pomPath = Join-Path $directory "pom.xml"
|
||||
$warmSettingsPath = Join-Path $directory "warm-settings.xml"
|
||||
$cachePath = Join-Path $directory "repository"
|
||||
$outputPath = Join-Path $directory "warm-effective-pom.xml"
|
||||
|
||||
# This project repository keeps the inspection plugin available offline
|
||||
# after the generated settings disable inherited plugin Central.
|
||||
@'
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>setup-java.test</groupId>
|
||||
<artifactId>repository-settings</artifactId>
|
||||
<version>1.0</version>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>inspection</id>
|
||||
<url>https://repo.maven.apache.org/maven2</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
</project>
|
||||
'@ | Set-Content -Path $pomPath
|
||||
'<settings />' | Set-Content -Path $warmSettingsPath
|
||||
|
||||
mvn --batch-mode --no-transfer-progress --settings $warmSettingsPath `
|
||||
"-Dmaven.repo.local=$cachePath" --file $pomPath `
|
||||
org.apache.maven.plugins:maven-help-plugin:3.5.1:effective-pom `
|
||||
"-Doutput=$outputPath"
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Could not prepare the Maven Help Plugin"
|
||||
}
|
||||
- name: Configure dependency and plugin repositories
|
||||
uses: ./
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '21'
|
||||
settings-path: ${{ runner.temp }}/maven-repository-test/settings
|
||||
mvn-server-credentials: |
|
||||
private:PRIVATE_USERNAME:PRIVATE_TOKEN
|
||||
mvn-repositories: ${{ matrix.repositories != 'plugins' && 'private:https://repo.example.invalid/maven:false' || '' }}
|
||||
mvn-plugin-repositories: ${{ matrix.repositories != 'dependencies' && 'private:https://repo.example.invalid/maven:false' || '' }}
|
||||
mvn-repositories-include-central: false
|
||||
- name: Validate generated settings and effective Maven repositories
|
||||
env:
|
||||
REPOSITORY_CONFIGURATION: ${{ matrix.repositories }}
|
||||
run: |
|
||||
$directory = Join-Path $env:RUNNER_TEMP "maven-repository-test"
|
||||
$settingsPath = Join-Path $directory "settings/settings.xml"
|
||||
[xml]$settings = Get-Content -Path $settingsPath -Raw
|
||||
$profiles = @($settings.settings.profiles.profile)
|
||||
if ($profiles.Count -ne 1 -or $profiles[0].id -ne "setup-java-repositories") {
|
||||
throw "Expected one repository profile"
|
||||
}
|
||||
$activeProfiles = @($settings.settings.activeProfiles.activeProfile)
|
||||
if ($activeProfiles.Count -ne 1 -or $activeProfiles[0] -ne "setup-java-repositories") {
|
||||
throw "Expected one active repository profile"
|
||||
}
|
||||
$server = $settings.settings.servers.server
|
||||
if ($server.id -ne "private" -or $server.username -ne '${env.PRIVATE_USERNAME}' -or $server.password -ne '${env.PRIVATE_TOKEN}') {
|
||||
throw "Repository credentials were not preserved"
|
||||
}
|
||||
|
||||
function Assert-Repositories($repositories, $expectPrivate, $context) {
|
||||
$central = @($repositories | Where-Object { $_.id -eq "central" })
|
||||
if ($central.Count -ne 1 -or $central[0].releases.enabled -ne "false" -or $central[0].snapshots.enabled -ne "false") {
|
||||
throw "$context did not disable inherited Maven Central"
|
||||
}
|
||||
$private = @($repositories | Where-Object { $_.id -eq "private" })
|
||||
$expectedCount = if ($expectPrivate) { 1 } else { 0 }
|
||||
if ($private.Count -ne $expectedCount) {
|
||||
throw "$context has incorrect private repositories"
|
||||
}
|
||||
if ($expectPrivate -and ($private[0].url -ne "https://repo.example.invalid/maven" -or $private[0].snapshots.enabled -ne "false")) {
|
||||
throw "$context has incorrect private repository settings"
|
||||
}
|
||||
}
|
||||
|
||||
$expectDependencies = $env:REPOSITORY_CONFIGURATION -ne "plugins"
|
||||
$expectPlugins = $env:REPOSITORY_CONFIGURATION -ne "dependencies"
|
||||
Assert-Repositories @($profiles[0].repositories.repository) $expectDependencies "Settings dependency repositories"
|
||||
Assert-Repositories @($profiles[0].pluginRepositories.pluginRepository) $expectPlugins "Settings plugin repositories"
|
||||
|
||||
$pomPath = Join-Path $directory "pom.xml"
|
||||
$cachePath = Join-Path $directory "repository"
|
||||
$outputPath = Join-Path $directory "effective-pom.xml"
|
||||
mvn --batch-mode --no-transfer-progress --offline --settings $settingsPath `
|
||||
"-Dmaven.repo.local=$cachePath" --file $pomPath `
|
||||
org.apache.maven.plugins:maven-help-plugin:3.5.1:effective-pom `
|
||||
"-Doutput=$outputPath"
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Could not inspect the effective Maven model"
|
||||
}
|
||||
[xml]$effectivePom = Get-Content -Path $outputPath -Raw
|
||||
Assert-Repositories @($effectivePom.project.repositories.repository) $expectDependencies "Effective dependency repositories"
|
||||
Assert-Repositories @($effectivePom.project.pluginRepositories.pluginRepository) $expectPlugins "Effective plugin repositories"
|
||||
|
||||
@@ -51,6 +51,15 @@ jobs:
|
||||
- distribution: microsoft
|
||||
os: macos-latest
|
||||
version: 25
|
||||
- distribution: microsoft
|
||||
os: windows-latest
|
||||
version: '25.0.5+0.2'
|
||||
- distribution: microsoft
|
||||
os: ubuntu-latest
|
||||
version: '25.0.5+0.2'
|
||||
- distribution: microsoft
|
||||
os: macos-latest
|
||||
version: '25.0.5+0.2'
|
||||
- distribution: kona
|
||||
os: windows-latest
|
||||
version: 25
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: "@actions/cache"
|
||||
version: 6.2.0
|
||||
version: 6.3.0
|
||||
type: npm
|
||||
summary: Actions cache lib
|
||||
homepage: https://github.com/actions/toolkit/tree/main/packages/cache
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: "@azure/core-client"
|
||||
version: 1.11.0
|
||||
version: 1.11.1
|
||||
type: npm
|
||||
summary: Core library for interfacing with AutoRest generated code
|
||||
homepage: https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/core/core-client/
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: "@azure/core-rest-pipeline"
|
||||
version: 1.23.0
|
||||
type: npm
|
||||
summary: Isomorphic client library for making HTTP requests in node.js and browser.
|
||||
homepage: https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/core/core-rest-pipeline/
|
||||
license: mit
|
||||
licenses:
|
||||
- sources: LICENSE
|
||||
text: |
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
notices: []
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
---
|
||||
name: "@azure/storage-blob"
|
||||
version: 12.33.0
|
||||
version: 12.31.0
|
||||
type: npm
|
||||
summary: Microsoft Azure Storage SDK for JavaScript - Blob
|
||||
homepage: https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/storage/storage-blob/README.md
|
||||
homepage: https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/storage/storage-blob/
|
||||
license: mit
|
||||
licenses:
|
||||
- sources: LICENSE
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: "@azure/storage-common"
|
||||
version: 12.4.1
|
||||
version: 12.5.0
|
||||
type: npm
|
||||
summary: Azure Storage Common Client Library for JavaScript
|
||||
homepage: https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/storage/storage-common/README.md
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: "@nodable/entities"
|
||||
version: 3.0.0
|
||||
version: 3.1.0
|
||||
type: npm
|
||||
summary: Entity parser for XML, HTML, External entites with security and NCR control
|
||||
homepage:
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: "@typespec/ts-http-runtime"
|
||||
version: 0.3.7
|
||||
version: 0.3.9
|
||||
type: npm
|
||||
summary: Isomorphic client library for making HTTP requests in node.js and browser.
|
||||
homepage: https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/core/ts-http-runtime/README.md
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: brace-expansion
|
||||
version: 5.0.9
|
||||
version: 5.0.12
|
||||
type: npm
|
||||
summary: Brace expansion as known from sh/bash
|
||||
homepage:
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: fast-xml-builder
|
||||
version: 1.3.0
|
||||
version: 1.3.1
|
||||
type: npm
|
||||
summary: Build XML from JSON without C/C++ based libraries
|
||||
homepage:
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: fast-xml-parser
|
||||
version: 5.11.0
|
||||
version: 5.12.0
|
||||
type: npm
|
||||
summary: Validate XML, Parse XML, Build XML without C/C++ based libraries
|
||||
homepage:
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: is-unsafe
|
||||
version: 2.0.0
|
||||
version: 2.0.3
|
||||
type: npm
|
||||
summary: Zero-dependency, DOM-free, pure predicate for detecting unsafe strings across
|
||||
HTML, XML, SVG, SQL, SHELL, and REGEX contexts
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: strnum
|
||||
version: 2.4.2
|
||||
version: 2.5.1
|
||||
type: npm
|
||||
summary: Parse String to Number based on configuration
|
||||
homepage:
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: undici
|
||||
version: 6.28.0
|
||||
version: 6.29.1
|
||||
type: npm
|
||||
summary: An HTTP/1.1 client, written from scratch for Node.js
|
||||
homepage: https://undici.nodejs.org
|
||||
|
||||
@@ -59,7 +59,7 @@ steps:
|
||||
- Warm JDK-cached jobs can reuse cached release metadata, avoiding vendor API calls while retaining a stale-metadata fallback for vendor outages and rate limits.
|
||||
- Maven configuration now supports multiple server credentials and custom dependency-resolution repositories.
|
||||
- Maven signing keys are imported into an isolated temporary GPG home instead of the runner's default keyring.
|
||||
- Set `problem-matcher: false` to disable Java compiler and uncaught-exception annotations.
|
||||
- Java compiler and uncaught-exception annotations are now disabled by default. Set `problem-matcher: true` to enable them.
|
||||
- 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.
|
||||
- Renamed environment-variable-name inputs so they are not mistaken for secret values:
|
||||
@@ -150,12 +150,12 @@ steps:
|
||||
| `java-version-file` | Path to `.java-version`, `.tool-versions`, or `.sdkmanrc`. Used when `java-version` is not set. | |
|
||||
| `distribution` | Java distribution keyword. Values are case-sensitive and must match one of the supported keywords below. Required unless `java-version-file` points to `.sdkmanrc` with a recognized distribution suffix. | |
|
||||
| `java-package` | Package variant such as `jdk`, `jre`, `jdk+fx`, `jre+fx`, `jdk+crac`, `jre+crac`, `jdk+jmods`, `jdk+jcef`, `jre+jcef`, `jdk+ft`, or `jre+ft`. Support varies by distribution. | `jdk` |
|
||||
| `architecture` | Package architecture. Canonical values are `x86`, `x64`, `armv7`, `aarch64`, `ppc64le`, `ppc64`, and `s390x`. Aliases `ia32`, `amd64`, `arm`, and `arm64` are normalized. | Runner architecture |
|
||||
| `architecture` | Package architecture. Canonical values are `x86`, `x64`, `armv7`, `aarch64`, `ppc64le`, `ppc64`, `riscv64`, and `s390x`. Aliases `ia32`, `amd64`, `arm`, and `arm64` are normalized. | Runner architecture |
|
||||
| `jdk-file` | Local compressed JDK archive. Requires `distribution: jdkfile`. | |
|
||||
| `check-latest` | Check remote metadata for the latest version satisfying the version spec before using the runner tool cache. | `false` |
|
||||
| `force-download` | Always download Java and replace any matching version in the tool cache. | `false` |
|
||||
| `set-default` | Add Java to `PATH` and set `JAVA_HOME`. When `false`, only version-specific `JAVA_HOME_<major>_<arch>` variables are set. | `true` |
|
||||
| `problem-matcher` | Register Java compiler and uncaught exception problem matchers. | `true` |
|
||||
| `problem-matcher` | Register Java compiler and uncaught exception problem matchers. | `false` |
|
||||
| `verify-signature` | Verify downloaded Java package signatures when supported. Explicitly setting this to `true` makes verification failures fatal. | Distribution-dependent; see [Download integrity and signatures](#download-integrity-and-signatures) |
|
||||
| `verify-signature-public-key` | One or more ASCII-armored GPG public keys used for signature verification. Concatenate multiple armored key blocks. Custom keys replace the bundled distribution keys. | |
|
||||
| `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 |
|
||||
@@ -168,9 +168,12 @@ steps:
|
||||
| `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` |
|
||||
| `mvn-server-credentials` | Multiline Maven server credentials in the format `server-id:USERNAME_ENV:PASSWORD_ENV`. Replaces the single server configured by the three inputs above when set. | |
|
||||
| `mvn-server-repository-origins` | Multiline Maven credential origins in the format `server-id:repository-origin`. Each origin must belong to a configured server ID. Requires Maven 3.10+. | |
|
||||
| `mvn-repositories` | Multiline Maven dependency repositories in the format `repository-id:repository-url:snapshots-enabled`. | |
|
||||
| `mvn-repositories-include-central` | Include Maven Central in the generated dependency repositories profile. When `false`, Central is disabled unless an explicit `central` repository is declared. | `true` |
|
||||
| `mvn-repositories-prioritize-central` | Place Maven Central before custom dependency repositories. Has no effect when Maven Central is excluded. | `true` |
|
||||
| `mvn-plugin-repositories` | Multiline Maven plugin repositories in the format `repository-id:repository-url:snapshots-enabled`. Can be used independently of `mvn-repositories`. | |
|
||||
| `mvn-mirrors` | Multiline Maven mirrors in the format `mirror-id:mirror-url:mirror-of`. Redirect matching dependency and plugin repositories; use a matching server ID for authentication. | |
|
||||
| `mvn-repositories-include-central` | Include Maven Central for dependency and plugin repositories in the generated profile. When `false`, Central is disabled for both types unless that type explicitly declares `central`. Has no effect when neither repository list is configured. | `true` |
|
||||
| `mvn-repositories-prioritize-central` | Place Maven Central before custom dependency and plugin repositories. Has no effect when Central is excluded or neither repository list is configured. | `true` |
|
||||
| `settings-path` | Directory where `settings.xml` is written. | `~/.m2` |
|
||||
| `overwrite-settings` | Overwrite an existing `settings.xml`. | `true` |
|
||||
| `gpg-private-key` | GPG private key to import into an isolated temporary keyring. | |
|
||||
@@ -222,6 +225,7 @@ Additional distribution notes:
|
||||
|
||||
- Oracle OpenJDK builds are archived after a limited number of releases and no longer receive security updates. To continue receiving security patches, use Oracle JDK or another vendor.
|
||||
- Azul Zulu maps `arm64` to `aarch64` when querying the Azul Metadata API.
|
||||
- Microsoft Build of OpenJDK uses Microsoft's live version manifest, falling back to the bundled version list when no release matches. Version `25.0.5.0.2` can also be requested as `25.0.5+0.2`.
|
||||
- GraalVM Community is available as `distribution: graalvm-community` for stable JDK 17 and later releases.
|
||||
- On Ubuntu runners, commands executed with `sudo` do not inherit the `JAVA_HOME` and `PATH` set by `setup-java` and may fall back to the system-default JDK.
|
||||
|
||||
@@ -233,7 +237,7 @@ Additional distribution notes:
|
||||
| --- | --- |
|
||||
| Major version | `8`, `11`, `17`, `21`, `25` |
|
||||
| Specific feature or patch version | `11.0`, `11.0.4`, `17.0`, `8.0.282+8` |
|
||||
| JEP 322 multi-field versions | `11.0.9.1`, `18.0.1.1` |
|
||||
| JEP 322 multi-field versions | `11.0.9.1`, `18.0.1.1`, `26.0.2.1+1` |
|
||||
| Early access | `15-ea`, `15.0.0-ea`, `27-ea` |
|
||||
| Latest stable GA release | `latest` |
|
||||
|
||||
@@ -481,10 +485,25 @@ steps:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
```
|
||||
|
||||
For dependencies hosted outside Maven Central, use `mvn-repositories` to add
|
||||
resolution repositories to an active profile in the generated `settings.xml`.
|
||||
Repository IDs can match `mvn-server-credentials` IDs when authentication is
|
||||
required. See [Resolving Maven dependencies from custom repositories](docs/advanced-usage.md#resolving-maven-dependencies-from-custom-repositories).
|
||||
For dependencies hosted outside Maven Central, use `mvn-repositories`; for build
|
||||
plugins, use `mvn-plugin-repositories`. Either input adds its own repository type
|
||||
to the same active profile in the generated `settings.xml`. Repository IDs can
|
||||
match `mvn-server-credentials` IDs when authentication is required.
|
||||
See [Resolving Maven dependencies from custom repositories](docs/advanced-usage.md#resolving-maven-dependencies-from-custom-repositories)
|
||||
and [Resolving Maven plugins from custom repositories](docs/advanced-usage.md#resolving-maven-plugins-from-custom-repositories).
|
||||
|
||||
To redirect repositories, including Central and repositories declared in project
|
||||
or parent POMs, use [`mvn-mirrors`](docs/advanced-usage.md#redirecting-maven-repositories-with-mirrors).
|
||||
It works independently of the repository inputs; a `mirrorOf` pattern of `*`
|
||||
routes all dependency and plugin repositories through the configured mirror.
|
||||
|
||||
> [!WARNING]
|
||||
> `mvn-repositories-include-central: false` now disables inherited Maven Central
|
||||
> for **both dependencies and plugins**, even in existing workflows that only
|
||||
> configure `mvn-repositories`. Builds relying on uncached plugins from Central
|
||||
> must configure `mvn-plugin-repositories` with an accessible plugin repository
|
||||
> or explicitly declare `central` there. Neither repository list configured
|
||||
> means the Central controls remain inactive.
|
||||
|
||||
### GPG signing
|
||||
|
||||
|
||||
+753
-18
@@ -63,12 +63,51 @@ const credentials = (id: string, username: string, password: string) => [
|
||||
const repositorySettings = (
|
||||
repositories: auth.MavenRepository[],
|
||||
includeCentral = true,
|
||||
prioritizeCentral = true
|
||||
prioritizeCentral = true,
|
||||
pluginRepositories: auth.MavenRepository[] = []
|
||||
): auth.MavenRepositorySettings => ({
|
||||
repositories,
|
||||
pluginRepositories,
|
||||
includeCentral,
|
||||
prioritizeCentral
|
||||
});
|
||||
const repositoryInputs = ['mvn-repositories', 'mvn-plugin-repositories'];
|
||||
const privateRepository = {
|
||||
id: 'private',
|
||||
url: 'https://repo.example.com/maven',
|
||||
snapshotsEnabled: true
|
||||
};
|
||||
const privateRepositoryXml = {
|
||||
id: privateRepository.id,
|
||||
url: privateRepository.url,
|
||||
snapshots: {enabled: 'true'}
|
||||
};
|
||||
const centralRepositoryXml = {
|
||||
id: 'central',
|
||||
url: 'https://repo.maven.apache.org/maven2',
|
||||
snapshots: {enabled: 'false'}
|
||||
};
|
||||
const disabledCentralRepositoryXml = {
|
||||
...centralRepositoryXml,
|
||||
releases: {enabled: 'false'}
|
||||
};
|
||||
const repositoryConfigurations = [
|
||||
{
|
||||
name: 'dependency-only',
|
||||
repositories: [privateRepository],
|
||||
pluginRepositories: []
|
||||
},
|
||||
{
|
||||
name: 'plugin-only',
|
||||
repositories: [],
|
||||
pluginRepositories: [privateRepository]
|
||||
},
|
||||
{
|
||||
name: 'combined',
|
||||
repositories: [privateRepository],
|
||||
pluginRepositories: [privateRepository]
|
||||
}
|
||||
];
|
||||
|
||||
describe('auth tests', () => {
|
||||
let spyOSHomedir: any;
|
||||
@@ -318,6 +357,21 @@ describe('auth tests', () => {
|
||||
).toEqual(expectedSettings);
|
||||
});
|
||||
|
||||
it('generates repository origins for a Maven server', () => {
|
||||
const settings = auth.generate([
|
||||
{
|
||||
id: 'central',
|
||||
usernameEnvVar: 'CENTRAL_USER',
|
||||
passwordEnvVar: 'CENTRAL_PASS',
|
||||
repositoryOrigins: ['https://central.sonatype.com']
|
||||
}
|
||||
]);
|
||||
|
||||
expect(settings).toContain(` <repositoryOrigins>
|
||||
<repositoryOrigin>https://central.sonatype.com</repositoryOrigin>
|
||||
</repositoryOrigins>`);
|
||||
});
|
||||
|
||||
it('does not add a gpg profile when the passphrase env var is the maven-gpg-plugin default', () => {
|
||||
const id = 'packages';
|
||||
const username = 'USER';
|
||||
@@ -460,6 +514,87 @@ describe('auth tests', () => {
|
||||
).toEqual(['first', 'second', 'central']);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
name: 'Central first by default',
|
||||
prioritizeCentral: undefined,
|
||||
expectedRepositories: [
|
||||
{
|
||||
id: 'central',
|
||||
url: 'https://repo.maven.apache.org/maven2',
|
||||
snapshots: {enabled: 'false'}
|
||||
},
|
||||
{
|
||||
id: 'first',
|
||||
url: 'https://first.example.com/plugins',
|
||||
snapshots: {enabled: 'false'}
|
||||
},
|
||||
{
|
||||
id: 'second',
|
||||
url: 'https://second.example.com/plugins',
|
||||
snapshots: {enabled: 'true'}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'custom plugins before Central',
|
||||
prioritizeCentral: false,
|
||||
expectedRepositories: [
|
||||
{
|
||||
id: 'first',
|
||||
url: 'https://first.example.com/plugins',
|
||||
snapshots: {enabled: 'false'}
|
||||
},
|
||||
{
|
||||
id: 'second',
|
||||
url: 'https://second.example.com/plugins',
|
||||
snapshots: {enabled: 'true'}
|
||||
},
|
||||
{
|
||||
id: 'central',
|
||||
url: 'https://repo.maven.apache.org/maven2',
|
||||
snapshots: {enabled: 'false'}
|
||||
}
|
||||
]
|
||||
}
|
||||
])(
|
||||
'generates plugin-only settings with $name',
|
||||
({prioritizeCentral, expectedRepositories}) => {
|
||||
const xml = auth.generate(
|
||||
[],
|
||||
undefined,
|
||||
repositorySettings([], true, prioritizeCentral, [
|
||||
{
|
||||
id: 'first',
|
||||
url: 'https://first.example.com/plugins',
|
||||
snapshotsEnabled: false
|
||||
},
|
||||
{
|
||||
id: 'second',
|
||||
url: 'https://second.example.com/plugins',
|
||||
snapshotsEnabled: true
|
||||
}
|
||||
])
|
||||
);
|
||||
|
||||
expect(parseXmlObject(xml)).toEqual(
|
||||
expect.objectContaining({
|
||||
settings: expect.objectContaining({
|
||||
profiles: {
|
||||
profile: {
|
||||
id: 'setup-java-repositories',
|
||||
pluginRepositories: {
|
||||
pluginRepository: expectedRepositories
|
||||
}
|
||||
}
|
||||
},
|
||||
activeProfiles: {activeProfile: 'setup-java-repositories'}
|
||||
})
|
||||
})
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
it('excludes Central from the generated repository profile when configured', () => {
|
||||
const parsed = parseXmlObject(
|
||||
auth.generate(
|
||||
@@ -491,6 +626,9 @@ describe('auth tests', () => {
|
||||
snapshots: {enabled: 'false'}
|
||||
}
|
||||
]);
|
||||
expect(
|
||||
parsed.settings.profiles.profile.pluginRepositories.pluginRepository
|
||||
).toEqual(disabledCentralRepositoryXml);
|
||||
});
|
||||
|
||||
it('combines repository and GPG configuration in shared profile blocks', () => {
|
||||
@@ -528,6 +666,9 @@ describe('auth tests', () => {
|
||||
snapshots: {enabled: 'false'}
|
||||
}
|
||||
]
|
||||
},
|
||||
pluginRepositories: {
|
||||
pluginRepository: disabledCentralRepositoryXml
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -541,6 +682,161 @@ describe('auth tests', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it.each(
|
||||
repositoryConfigurations.flatMap(configuration =>
|
||||
[
|
||||
{includeCentral: true, prioritizeCentral: true},
|
||||
{includeCentral: true, prioritizeCentral: false},
|
||||
{includeCentral: false, prioritizeCentral: true},
|
||||
{includeCentral: false, prioritizeCentral: false}
|
||||
].map(controls => ({...configuration, ...controls}))
|
||||
)
|
||||
)(
|
||||
'generates $name repositories with includeCentral=$includeCentral and prioritizeCentral=$prioritizeCentral',
|
||||
({repositories, pluginRepositories, includeCentral, prioritizeCentral}) => {
|
||||
const xml = auth.generate(
|
||||
credentials('private', 'USERNAME', 'PASSWORD'),
|
||||
undefined,
|
||||
repositorySettings(
|
||||
repositories,
|
||||
includeCentral,
|
||||
prioritizeCentral,
|
||||
pluginRepositories
|
||||
)
|
||||
);
|
||||
const expectedEntries = (configured: boolean) => {
|
||||
const entries = includeCentral
|
||||
? configured
|
||||
? prioritizeCentral
|
||||
? [centralRepositoryXml, privateRepositoryXml]
|
||||
: [privateRepositoryXml, centralRepositoryXml]
|
||||
: []
|
||||
: configured
|
||||
? [privateRepositoryXml, disabledCentralRepositoryXml]
|
||||
: [disabledCentralRepositoryXml];
|
||||
return entries.length === 1 ? entries[0] : entries;
|
||||
};
|
||||
const includeRepositories = repositories.length > 0 || !includeCentral;
|
||||
const includePluginRepositories =
|
||||
pluginRepositories.length > 0 || !includeCentral;
|
||||
|
||||
expect(parseXmlObject(xml)).toMatchObject({
|
||||
settings: {
|
||||
profiles: {
|
||||
profile: {
|
||||
id: 'setup-java-repositories',
|
||||
...(includeRepositories
|
||||
? {
|
||||
repositories: {
|
||||
repository: expectedEntries(repositories.length > 0)
|
||||
}
|
||||
}
|
||||
: {}),
|
||||
...(includePluginRepositories
|
||||
? {
|
||||
pluginRepositories: {
|
||||
pluginRepository: expectedEntries(
|
||||
pluginRepositories.length > 0
|
||||
)
|
||||
}
|
||||
}
|
||||
: {})
|
||||
}
|
||||
},
|
||||
activeProfiles: {activeProfile: 'setup-java-repositories'}
|
||||
}
|
||||
});
|
||||
if (!includeRepositories) {
|
||||
expect(xml).not.toContain('<repositories>');
|
||||
}
|
||||
if (!includePluginRepositories) {
|
||||
expect(xml).not.toContain('<pluginRepositories>');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
it.each(repositoryConfigurations)(
|
||||
'handles explicit Central independently for $name repositories',
|
||||
({repositories, pluginRepositories}) => {
|
||||
const central = {
|
||||
id: 'central',
|
||||
url: 'https://mirror.example.com/maven',
|
||||
snapshotsEnabled: true
|
||||
};
|
||||
const explicitCentralXml = {
|
||||
id: central.id,
|
||||
url: central.url,
|
||||
snapshots: {enabled: 'true'}
|
||||
};
|
||||
const xml = auth.generate(
|
||||
[],
|
||||
undefined,
|
||||
repositorySettings(
|
||||
repositories.length ? [central] : [],
|
||||
false,
|
||||
true,
|
||||
pluginRepositories.length ? [central] : []
|
||||
)
|
||||
);
|
||||
|
||||
expect(parseXmlObject(xml)).toMatchObject({
|
||||
settings: {
|
||||
profiles: {
|
||||
profile: {
|
||||
repositories: {
|
||||
repository: repositories.length
|
||||
? explicitCentralXml
|
||||
: disabledCentralRepositoryXml
|
||||
},
|
||||
pluginRepositories: {
|
||||
pluginRepository: pluginRepositories.length
|
||||
? explicitCentralXml
|
||||
: disabledCentralRepositoryXml
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
expect(xml.match(/<id>central<\/id>/g)).toHaveLength(2);
|
||||
}
|
||||
);
|
||||
|
||||
it('escapes plugin repository values while preserving parsed semantics', () => {
|
||||
const repository = {
|
||||
id: `private&<>"'é`,
|
||||
url: `https://repo.example.com:8443/a?x=1&y=<value>"'é`,
|
||||
snapshotsEnabled: false
|
||||
};
|
||||
const xml = auth.generate(
|
||||
credentials(repository.id, 'USERNAME', 'PASSWORD'),
|
||||
undefined,
|
||||
repositorySettings([], true, true, [repository])
|
||||
);
|
||||
|
||||
expect(parseXmlObject(xml)).toMatchObject({
|
||||
settings: {
|
||||
servers: {server: {id: repository.id}},
|
||||
profiles: {
|
||||
profile: {
|
||||
pluginRepositories: {
|
||||
pluginRepository: [
|
||||
centralRepositoryXml,
|
||||
{
|
||||
id: repository.id,
|
||||
url: repository.url,
|
||||
snapshots: {enabled: 'false'}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
expect(xml).toContain('private&<>');
|
||||
expect(xml).toContain('x=1&y=<value>');
|
||||
expect(xml).not.toContain('<repositories>');
|
||||
});
|
||||
|
||||
it('escapes repository values while preserving parsed semantics', () => {
|
||||
const repository = {
|
||||
id: `private&<>"'é`,
|
||||
@@ -586,15 +882,198 @@ describe('auth tests', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('parses Maven repositories while preserving colons in URLs', () => {
|
||||
it.each([
|
||||
'*',
|
||||
'central',
|
||||
'external:*',
|
||||
'external:http:*',
|
||||
'*,!internal',
|
||||
'external:*,!internal',
|
||||
'!internal,external:http:*',
|
||||
'external:*,!external:http:*'
|
||||
])('preserves the mirror selector %s and URL colons', mirrorOf => {
|
||||
expect(
|
||||
auth.parseMavenMirrors([
|
||||
'',
|
||||
' \t ',
|
||||
` private : https://[2001:db8::1]:8443/maven : ${mirrorOf} `,
|
||||
'public:https://repo.example.com:443/releases:central'
|
||||
])
|
||||
).toEqual([
|
||||
{
|
||||
id: 'private',
|
||||
url: 'https://[2001:db8::1]:8443/maven',
|
||||
mirrorOf
|
||||
},
|
||||
{
|
||||
id: 'public',
|
||||
url: 'https://repo.example.com:443/releases',
|
||||
mirrorOf: 'central'
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it.each([
|
||||
'https://repo.example.com:8443',
|
||||
'https://repo.example.com:8443/maven',
|
||||
'https://[2001:db8::1]',
|
||||
'https://[2001:db8::1]/maven',
|
||||
'https://[2001:db8::1]:8443',
|
||||
'https://[2001:db8::1]:8443/maven',
|
||||
'https://repo.example.com/maven?redirect=https%3A//upstream.example.com%3A8443',
|
||||
'https://repo.example.com/maven#urn%3Aartifact',
|
||||
'https://repo.example.com/maven?redirect=https://upstream.example.com:8443',
|
||||
'https://repo.example.com/maven#urn:artifact'
|
||||
])('preserves the complete mirror URL %s with an explicit selector', url => {
|
||||
expect(
|
||||
auth.parseMavenMirrors([`private:${url}:external:http:*,!internal`])
|
||||
).toEqual([{id: 'private', url, mirrorOf: 'external:http:*,!internal'}]);
|
||||
});
|
||||
|
||||
it.each([
|
||||
'private',
|
||||
'private:https://repo.example.com',
|
||||
'private:https://repo.example.com:8443',
|
||||
'private:https://repo.example.com:8443/maven',
|
||||
'private:https://[2001:db8::1]',
|
||||
'private:https://[2001:db8::1]/maven',
|
||||
'private:https://[2001:db8::1]:8443',
|
||||
'private:https://[2001:db8::1]:8443/maven',
|
||||
'private:repo.example.com:central',
|
||||
':https://repo.example.com:*',
|
||||
'private::*',
|
||||
'private:https://repo.example.com:',
|
||||
'private: :*',
|
||||
'private:https://repo.example.com: '
|
||||
])('rejects malformed mirror entry %s with a line number', entry => {
|
||||
expect(() => auth.parseMavenMirrors(['', entry])).toThrow(
|
||||
'Invalid mvn-mirrors entry at line 2.'
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects duplicate mirror IDs after trimming', () => {
|
||||
expect(() =>
|
||||
auth.parseMavenMirrors([
|
||||
'private:https://first.example.com:*',
|
||||
' private :https://second.example.com:central'
|
||||
])
|
||||
).toThrow("Duplicate mirror-id 'private' in mvn-mirrors input at line 2");
|
||||
});
|
||||
|
||||
it('leaves settings unchanged for absent or blank mirrors', () => {
|
||||
const servers = credentials('github', 'USERNAME', 'PASSWORD');
|
||||
expect(auth.parseMavenMirrors(['', ' ', '\t'])).toEqual([]);
|
||||
expect(auth.generate(servers, undefined, undefined, [])).toBe(
|
||||
auth.generate(servers)
|
||||
);
|
||||
expect(auth.generate(servers)).not.toContain('<mirrors>');
|
||||
});
|
||||
|
||||
it('generates public mirrors without credentials or repository profiles', () => {
|
||||
const mirrors = auth.parseMavenMirrors([
|
||||
'public:https://repo.example.com:external:*',
|
||||
'fallback:https://other.example.com:central'
|
||||
]);
|
||||
const xml = auth.generate([], undefined, undefined, mirrors);
|
||||
expect(parseXmlObject(xml)).toMatchObject({
|
||||
settings: {mirrors: {mirror: mirrors}}
|
||||
});
|
||||
expect(xml).not.toContain('<profiles>');
|
||||
expect(xml).not.toContain('<activeProfiles>');
|
||||
});
|
||||
|
||||
it('escapes all mirror fields as XML text', () => {
|
||||
const mirror = {
|
||||
id: 'private<&>',
|
||||
url: 'https://repo.example.com/maven?a=1&b=<value>',
|
||||
mirrorOf: '*,!internal<&>'
|
||||
};
|
||||
const xml = auth.generate([], undefined, undefined, [mirror]);
|
||||
expect(xml).toContain('<id>private<&></id>');
|
||||
expect(xml).toContain('a=1&b=<value>');
|
||||
expect(xml).toContain('<mirrorOf>*,!internal<&></mirrorOf>');
|
||||
expect(parseXmlObject(xml)).toMatchObject({
|
||||
settings: {mirrors: {mirror}}
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
{combined: false, overwrite: true},
|
||||
{combined: true, overwrite: true},
|
||||
{combined: true, overwrite: false}
|
||||
])(
|
||||
'configures mirrors with combined=$combined and overwrite=$overwrite',
|
||||
async ({combined, overwrite}) => {
|
||||
const directory = path.join(m2Dir, 'mirrors');
|
||||
const location = path.join(directory, MVN_SETTINGS_FILE);
|
||||
fs.mkdirSync(directory, {recursive: true});
|
||||
fs.writeFileSync(location, 'EXISTING SETTINGS');
|
||||
const url = 'https://maven.example.com/releases';
|
||||
const inputs: Record<string, string> = {
|
||||
'settings-path': directory,
|
||||
'overwrite-settings': String(overwrite),
|
||||
'mvn-repositories-include-central': 'false',
|
||||
...(combined ? {'gpg-passphrase-env-var': 'GPG_PASSPHRASE'} : {})
|
||||
};
|
||||
const multilineInputs: Record<string, string[]> = {
|
||||
'mvn-server-credentials': ['private:PRIVATE_USERNAME:PRIVATE_TOKEN'],
|
||||
'mvn-mirrors': [`private:${url}:*`],
|
||||
...(combined
|
||||
? {
|
||||
'mvn-repositories': [`private:${url}:false`],
|
||||
'mvn-plugin-repositories': [`private:${url}:false`]
|
||||
}
|
||||
: {})
|
||||
};
|
||||
jest.mocked(core.getInput).mockImplementation(name => inputs[name] ?? '');
|
||||
jest
|
||||
.mocked(core.getMultilineInput)
|
||||
.mockImplementation(name => multilineInputs[name] ?? []);
|
||||
|
||||
await auth.configureAuthentication();
|
||||
|
||||
const xml = fs.readFileSync(location, 'utf8');
|
||||
if (!overwrite) {
|
||||
expect(xml).toBe('EXISTING SETTINGS');
|
||||
return;
|
||||
}
|
||||
expect(parseXmlObject(xml)).toMatchObject({
|
||||
settings: {
|
||||
mirrors: {mirror: {id: 'private', url, mirrorOf: '*'}},
|
||||
servers: {
|
||||
server: {
|
||||
id: 'private',
|
||||
username: '${env.PRIVATE_USERNAME}',
|
||||
password: '${env.PRIVATE_TOKEN}'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (combined) {
|
||||
expect(xml).toContain('<repositories>');
|
||||
expect(xml).toContain('<pluginRepositories>');
|
||||
expect(xml).toContain('<gpg.passphraseEnvName>GPG_PASSPHRASE');
|
||||
} else {
|
||||
expect(xml).not.toContain('<profiles>');
|
||||
expect(core.getInput).not.toHaveBeenCalledWith(
|
||||
'mvn-repositories-include-central'
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
it.each(repositoryInputs)(
|
||||
'parses %s while preserving colons in URLs',
|
||||
inputName => {
|
||||
expect(
|
||||
auth.parseMavenRepositories(
|
||||
[
|
||||
'',
|
||||
' private : https://repo.example.com:8443/maven : true ',
|
||||
' private : https://repo.example.com:8443/maven : TRUE ',
|
||||
'releases:https://repo.example.com/releases:false'
|
||||
],
|
||||
true
|
||||
true,
|
||||
inputName
|
||||
)
|
||||
).toEqual([
|
||||
{
|
||||
@@ -608,9 +1087,11 @@ describe('auth tests', () => {
|
||||
snapshotsEnabled: false
|
||||
}
|
||||
]);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
it.each([
|
||||
it.each(
|
||||
[
|
||||
{
|
||||
entries: ['private'],
|
||||
error:
|
||||
@@ -626,41 +1107,66 @@ describe('auth tests', () => {
|
||||
error:
|
||||
'Invalid mvn-repositories entry at line 1. repository-id, repository URL, and snapshots-enabled are required'
|
||||
},
|
||||
{
|
||||
entries: ['private:https://repo.example.com:'],
|
||||
error:
|
||||
'Invalid mvn-repositories entry at line 1. repository-id, repository URL, and snapshots-enabled are required'
|
||||
},
|
||||
{
|
||||
entries: ['', 'private:https://repo.example.com:true', ' :url:false'],
|
||||
error:
|
||||
'Invalid mvn-repositories entry at line 3. repository-id, repository URL, and snapshots-enabled are required'
|
||||
},
|
||||
{
|
||||
entries: ['private:https://repo.example.com:sometimes'],
|
||||
error:
|
||||
"Invalid snapshots-enabled value 'sometimes' in mvn-repositories entry at line 1. Expected true or false"
|
||||
}
|
||||
])('rejects malformed Maven repositories: $entries', ({entries, error}) => {
|
||||
expect(() => auth.parseMavenRepositories(entries, true)).toThrow(error);
|
||||
].flatMap(testCase =>
|
||||
repositoryInputs.map(inputName => ({
|
||||
...testCase,
|
||||
inputName,
|
||||
error: testCase.error.replace('mvn-repositories', inputName)
|
||||
}))
|
||||
)
|
||||
)('rejects malformed $inputName: $entries', ({entries, error, inputName}) => {
|
||||
expect(() => auth.parseMavenRepositories(entries, true, inputName)).toThrow(
|
||||
error
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects duplicate Maven repository ids', () => {
|
||||
it.each(repositoryInputs)('rejects duplicate IDs in %s', inputName => {
|
||||
expect(() =>
|
||||
auth.parseMavenRepositories(
|
||||
[
|
||||
'private:https://first.example.com:false',
|
||||
'private:https://second.example.com:true'
|
||||
],
|
||||
true
|
||||
true,
|
||||
inputName
|
||||
)
|
||||
).toThrow("Duplicate repository-id 'private' in mvn-repositories input");
|
||||
).toThrow(`Duplicate repository-id 'private' in ${inputName} input`);
|
||||
});
|
||||
|
||||
it('reserves the Central repository id only when automatic Central inclusion is enabled', () => {
|
||||
it.each(repositoryInputs)(
|
||||
'reserves the Central ID in %s only when automatic inclusion is enabled',
|
||||
inputName => {
|
||||
const central = 'central:https://custom.example.com/maven:false';
|
||||
|
||||
expect(() => auth.parseMavenRepositories([central], true)).toThrow(
|
||||
expect(() =>
|
||||
auth.parseMavenRepositories([central], true, inputName)
|
||||
).toThrow(
|
||||
"Repository-id 'central' is reserved when mvn-repositories-include-central is enabled"
|
||||
);
|
||||
expect(auth.parseMavenRepositories([central], false)).toEqual([
|
||||
expect(auth.parseMavenRepositories([central], false, inputName)).toEqual([
|
||||
{
|
||||
id: 'central',
|
||||
url: 'https://custom.example.com/maven',
|
||||
snapshotsEnabled: false
|
||||
}
|
||||
]);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
it('uses an explicit Central repository instead of adding a disabled override', () => {
|
||||
const parsed = parseXmlObject(
|
||||
@@ -706,16 +1212,138 @@ describe('auth tests', () => {
|
||||
snapshotsEnabled: true
|
||||
}
|
||||
],
|
||||
pluginRepositories: [],
|
||||
includeCentral: false,
|
||||
prioritizeCentral: false
|
||||
});
|
||||
});
|
||||
|
||||
it('does not read repository controls when no repositories are configured', () => {
|
||||
(
|
||||
core.getInput as jest.MockedFunction<typeof core.getInput>
|
||||
).mockReturnValue('false');
|
||||
expect(auth.getMavenRepositorySettings()).toBeUndefined();
|
||||
expect(core.getInput).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('ignores blank dependency and plugin repository inputs', () => {
|
||||
(
|
||||
core.getMultilineInput as jest.MockedFunction<
|
||||
typeof core.getMultilineInput
|
||||
>
|
||||
).mockReturnValue(['', ' ', '\t']);
|
||||
|
||||
expect(auth.getMavenRepositorySettings()).toBeUndefined();
|
||||
expect(core.getInput).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each(repositoryConfigurations)(
|
||||
'reads $name inputs with default Central controls and shared IDs',
|
||||
({repositories, pluginRepositories}) => {
|
||||
(
|
||||
core.getInput as jest.MockedFunction<typeof core.getInput>
|
||||
).mockReturnValue('');
|
||||
(
|
||||
core.getMultilineInput as jest.MockedFunction<
|
||||
typeof core.getMultilineInput
|
||||
>
|
||||
).mockImplementation(name => {
|
||||
const configured =
|
||||
name === 'mvn-repositories' ? repositories : pluginRepositories;
|
||||
return configured.map(
|
||||
repository =>
|
||||
`${repository.id}:${repository.url}:${repository.snapshotsEnabled}`
|
||||
);
|
||||
});
|
||||
|
||||
expect(auth.getMavenRepositorySettings()).toEqual({
|
||||
repositories,
|
||||
pluginRepositories,
|
||||
includeCentral: true,
|
||||
prioritizeCentral: true
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
it.each([true, false])(
|
||||
'configures plugin repositories with credentials, GPG, a custom path, and overwrite=%s',
|
||||
async overwriteSettings => {
|
||||
const directory = path.join(m2Dir, 'custom');
|
||||
const location = path.join(directory, MVN_SETTINGS_FILE);
|
||||
fs.mkdirSync(directory, {recursive: true});
|
||||
fs.writeFileSync(location, 'EXISTING SETTINGS');
|
||||
(
|
||||
core.getInput as jest.MockedFunction<typeof core.getInput>
|
||||
).mockImplementation(name => {
|
||||
const inputs: Record<string, string> = {
|
||||
'settings-path': directory,
|
||||
'overwrite-settings': String(overwriteSettings),
|
||||
'gpg-passphrase-env-var': 'GPG_PASSPHRASE',
|
||||
'mvn-repositories-include-central': 'false'
|
||||
};
|
||||
return inputs[name] ?? '';
|
||||
});
|
||||
(
|
||||
core.getMultilineInput as jest.MockedFunction<
|
||||
typeof core.getMultilineInput
|
||||
>
|
||||
).mockImplementation(name => {
|
||||
const inputs: Record<string, string[]> = {
|
||||
'mvn-server-credentials': ['private:USERNAME:PASSWORD'],
|
||||
'mvn-server-repository-origins': ['private:https://repo.example.com'],
|
||||
'mvn-plugin-repositories': [
|
||||
'private:https://repo.example.com/maven:true'
|
||||
]
|
||||
};
|
||||
return inputs[name] ?? [];
|
||||
});
|
||||
|
||||
await auth.configureAuthentication();
|
||||
|
||||
const xml = fs.readFileSync(location, 'utf8');
|
||||
if (!overwriteSettings) {
|
||||
expect(xml).toBe('EXISTING SETTINGS');
|
||||
return;
|
||||
}
|
||||
expect(parseXmlObject(xml)).toMatchObject({
|
||||
settings: {
|
||||
servers: {
|
||||
server: {
|
||||
id: 'private',
|
||||
username: '${env.USERNAME}',
|
||||
password: '${env.PASSWORD}',
|
||||
repositoryOrigins: {
|
||||
repositoryOrigin: 'https://repo.example.com'
|
||||
}
|
||||
}
|
||||
},
|
||||
profiles: {
|
||||
profile: [
|
||||
{
|
||||
id: 'setup-java-repositories',
|
||||
repositories: {repository: disabledCentralRepositoryXml},
|
||||
pluginRepositories: {
|
||||
pluginRepository: [
|
||||
privateRepositoryXml,
|
||||
disabledCentralRepositoryXml
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'setup-java-gpg',
|
||||
properties: {'gpg.passphraseEnvName': 'GPG_PASSPHRASE'}
|
||||
}
|
||||
]
|
||||
},
|
||||
activeProfiles: {
|
||||
activeProfile: ['setup-java-repositories', 'setup-java-gpg']
|
||||
}
|
||||
}
|
||||
});
|
||||
expect(fs.existsSync(settingsFile)).toBe(false);
|
||||
}
|
||||
);
|
||||
|
||||
it.each([
|
||||
{
|
||||
entries: ['releases:RELEASES_USERNAME'],
|
||||
@@ -749,16 +1377,23 @@ describe('auth tests', () => {
|
||||
});
|
||||
|
||||
it('uses multiline Maven server credentials instead of single-server inputs', () => {
|
||||
(core.getMultilineInput as jest.Mock).mockReturnValue([
|
||||
(core.getMultilineInput as jest.Mock).mockImplementation((name: string) =>
|
||||
name === 'mvn-server-credentials'
|
||||
? [
|
||||
'releases:RELEASES_USERNAME:RELEASES_PASSWORD',
|
||||
'snapshots:SNAPSHOTS_USERNAME:SNAPSHOTS_PASSWORD'
|
||||
]);
|
||||
]
|
||||
: name === 'mvn-server-repository-origins'
|
||||
? ['releases:https://repo.example.com']
|
||||
: []
|
||||
);
|
||||
|
||||
expect(auth.getMavenServerSettings()).toEqual([
|
||||
{
|
||||
id: 'releases',
|
||||
usernameEnvVar: 'RELEASES_USERNAME',
|
||||
passwordEnvVar: 'RELEASES_PASSWORD'
|
||||
passwordEnvVar: 'RELEASES_PASSWORD',
|
||||
repositoryOrigins: ['https://repo.example.com']
|
||||
},
|
||||
{
|
||||
id: 'snapshots',
|
||||
@@ -803,6 +1438,106 @@ describe('auth tests', () => {
|
||||
expect(core.warning).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('adds normalized repository origins to matching Maven servers', () => {
|
||||
expect(
|
||||
auth.addMavenServerRepositoryOrigins(
|
||||
[
|
||||
{
|
||||
id: 'central',
|
||||
usernameEnvVar: 'CENTRAL_USER',
|
||||
passwordEnvVar: 'CENTRAL_PASS'
|
||||
},
|
||||
{
|
||||
id: 'packages',
|
||||
usernameEnvVar: 'GITHUB_ACTOR',
|
||||
passwordEnvVar: 'GITHUB_TOKEN'
|
||||
}
|
||||
],
|
||||
[
|
||||
'central:https://central.sonatype.com/',
|
||||
'central:https://central.sonatype.com',
|
||||
'central:https://central.sonatype.com:443',
|
||||
'packages:https://maven.pkg.github.com',
|
||||
'packages:ssh://packages.example.com:22',
|
||||
'packages:ftp://packages.example.com:21'
|
||||
]
|
||||
)
|
||||
).toEqual([
|
||||
{
|
||||
id: 'central',
|
||||
usernameEnvVar: 'CENTRAL_USER',
|
||||
passwordEnvVar: 'CENTRAL_PASS',
|
||||
repositoryOrigins: ['https://central.sonatype.com']
|
||||
},
|
||||
{
|
||||
id: 'packages',
|
||||
usernameEnvVar: 'GITHUB_ACTOR',
|
||||
passwordEnvVar: 'GITHUB_TOKEN',
|
||||
repositoryOrigins: [
|
||||
'https://maven.pkg.github.com',
|
||||
'ssh://packages.example.com:22',
|
||||
'ftp://packages.example.com:21'
|
||||
]
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
entries: ['central'],
|
||||
error:
|
||||
'Invalid mvn-server-repository-origins entry at line 1. Expected format: server-id:repository-origin'
|
||||
},
|
||||
{
|
||||
entries: ['other:https://repo.example.com'],
|
||||
error:
|
||||
"Unknown server-id 'other' in mvn-server-repository-origins at line 1"
|
||||
},
|
||||
{
|
||||
entries: ['central:https://repo.example.com/path'],
|
||||
error:
|
||||
"Invalid repository origin 'https://repo.example.com/path' in mvn-server-repository-origins at line 1"
|
||||
},
|
||||
{
|
||||
entries: ['central:https:repo.example.com'],
|
||||
error:
|
||||
"Invalid repository origin 'https:repo.example.com' in mvn-server-repository-origins at line 1"
|
||||
},
|
||||
{
|
||||
entries: ['central:https:/repo.example.com'],
|
||||
error:
|
||||
"Invalid repository origin 'https:/repo.example.com' in mvn-server-repository-origins at line 1"
|
||||
},
|
||||
{
|
||||
entries: ['central:https://repo.example.com?'],
|
||||
error:
|
||||
"Invalid repository origin 'https://repo.example.com?' in mvn-server-repository-origins at line 1"
|
||||
},
|
||||
{
|
||||
entries: ['central:https://repo.example.com#'],
|
||||
error:
|
||||
"Invalid repository origin 'https://repo.example.com#' in mvn-server-repository-origins at line 1"
|
||||
},
|
||||
{
|
||||
entries: ['central:https://@repo.example.com'],
|
||||
error:
|
||||
"Invalid repository origin 'https://@repo.example.com' in mvn-server-repository-origins at line 1"
|
||||
}
|
||||
])('rejects invalid Maven server repository origins', ({entries, error}) => {
|
||||
expect(() =>
|
||||
auth.addMavenServerRepositoryOrigins(
|
||||
[
|
||||
{
|
||||
id: 'central',
|
||||
usernameEnvVar: 'CENTRAL_USER',
|
||||
passwordEnvVar: 'CENTRAL_PASS'
|
||||
}
|
||||
],
|
||||
entries
|
||||
)
|
||||
).toThrow(error);
|
||||
});
|
||||
|
||||
function xmlElementText(xml: string, tagName: string): string {
|
||||
const match = new RegExp(`<${tagName}>([\\s\\S]*?)</${tagName}>`).exec(xml);
|
||||
expect(match).not.toBeNull();
|
||||
|
||||
@@ -1758,6 +1758,8 @@ describe('normalizeVersion', () => {
|
||||
['11.0.9.1', {version: '11.0.9+1', stable: true, latest: false}],
|
||||
['12.0.2.1.0', {version: '12.0.2+1.0', stable: true, latest: false}],
|
||||
['18.0.1.1-ea', {version: '18.0.1+1', stable: false, latest: false}],
|
||||
['26.0.2.1+1', {version: '26.0.2+1.1', stable: true, latest: false}],
|
||||
['25.0.4.1+1', {version: '25.0.4+1.1', stable: true, latest: false}],
|
||||
['latest', {version: 'x', stable: true, latest: true}],
|
||||
['LATEST', {version: 'x', stable: true, latest: true}],
|
||||
[' Latest ', {version: 'x', stable: true, latest: true}]
|
||||
|
||||
@@ -244,8 +244,9 @@ describe('getAvailableVersions', () => {
|
||||
).rejects.toThrow('Early access versions are not supported');
|
||||
});
|
||||
|
||||
it('with non major version expect to throw not supported error', async () => {
|
||||
const version = '18.0.1';
|
||||
it.each(['18.0.1', '17.0.13.11', '17.0.13.11.1.1'])(
|
||||
'rejects partial or malformed release %s',
|
||||
async version => {
|
||||
const distribution = new CorrettoDistribution({
|
||||
version,
|
||||
architecture: 'x64',
|
||||
@@ -256,7 +257,269 @@ describe('getAvailableVersions', () => {
|
||||
|
||||
await expect(
|
||||
distribution['findPackageForDownload'](version)
|
||||
).rejects.toThrow('Only major versions are supported');
|
||||
).rejects.toThrow(
|
||||
'Specify a major version or a full Corretto release, such as 25.0.5.0.10'
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
it.each([
|
||||
[
|
||||
'25.0.5.0.10',
|
||||
'linux',
|
||||
'x64',
|
||||
'jdk',
|
||||
'25.0.5+0.10',
|
||||
'https://corretto.aws/downloads/resources/25.0.5.0.10/amazon-corretto-25.0.5.0.10-linux-x64.tar.gz'
|
||||
],
|
||||
[
|
||||
'25.0.5+0.10',
|
||||
'macos',
|
||||
'arm64',
|
||||
'jdk',
|
||||
'25.0.5+0.10',
|
||||
'https://corretto.aws/downloads/resources/25.0.5.0.10/amazon-corretto-25.0.5.0.10-macosx-aarch64.tar.gz'
|
||||
],
|
||||
[
|
||||
'17.0.2.8.1',
|
||||
'windows',
|
||||
'x64',
|
||||
'jdk',
|
||||
'17.0.2+8.1',
|
||||
'https://corretto.aws/downloads/resources/17.0.2.8.1/amazon-corretto-17.0.2.8.1-windows-x64-jdk.zip'
|
||||
],
|
||||
[
|
||||
'25.0.5.0.10',
|
||||
'alpine',
|
||||
'aarch64',
|
||||
'jdk',
|
||||
'25.0.5+0.10',
|
||||
'https://corretto.aws/downloads/resources/25.0.5.0.10/amazon-corretto-25.0.5.0.10-alpine-linux-aarch64.tar.gz'
|
||||
],
|
||||
[
|
||||
'11.0.14.10.1',
|
||||
'linux',
|
||||
'armv7',
|
||||
'jdk',
|
||||
'11.0.14+10.1',
|
||||
'https://corretto.aws/downloads/resources/11.0.14.10.1/amazon-corretto-11.0.14.10.1-linux-armv7.tar.gz'
|
||||
],
|
||||
[
|
||||
'8.472.8+1',
|
||||
'linux',
|
||||
'x64',
|
||||
'jdk',
|
||||
'8.472.8+1',
|
||||
'https://corretto.aws/downloads/resources/8.472.08.1/amazon-corretto-8.472.08.1-linux-x64.tar.gz'
|
||||
],
|
||||
[
|
||||
'8.472.08.1',
|
||||
'linux',
|
||||
'x64',
|
||||
'jdk',
|
||||
'8.472.8+1',
|
||||
'https://corretto.aws/downloads/resources/8.472.08.1/amazon-corretto-8.472.08.1-linux-x64.tar.gz'
|
||||
],
|
||||
[
|
||||
'8.472.08.1',
|
||||
'windows',
|
||||
'x86',
|
||||
'jre',
|
||||
'8.472.8+1',
|
||||
'https://corretto.aws/downloads/resources/8.472.08.1/amazon-corretto-8.472.08.1-windows-x86-jre.zip'
|
||||
]
|
||||
])(
|
||||
'resolves exact release %s on %s/%s (%s)',
|
||||
async (
|
||||
requestedVersion,
|
||||
platform,
|
||||
architecture,
|
||||
packageType,
|
||||
expectedVersion,
|
||||
url
|
||||
) => {
|
||||
const checksum = 'a'.repeat(64);
|
||||
spyHttpClient.mockResolvedValue({
|
||||
statusCode: 200,
|
||||
headers: {},
|
||||
result: {
|
||||
body: `|JDK|[archive](${url})|\`md5\` /<br /> \`${checksum}\`|`
|
||||
}
|
||||
});
|
||||
const distribution = new CorrettoDistribution({
|
||||
version: requestedVersion,
|
||||
architecture,
|
||||
packageType,
|
||||
checkLatest: false
|
||||
});
|
||||
mockPlatform(distribution, platform);
|
||||
|
||||
const release = await distribution['findPackageForDownload'](
|
||||
distribution['version']
|
||||
);
|
||||
const rawVersion = new URL(url).pathname.split('/')[3];
|
||||
const source = `https://api.github.com/repos/corretto/corretto-${rawVersion.split('.')[0]}/releases/tags/${rawVersion}`;
|
||||
expect(release).toEqual({
|
||||
version: expectedVersion,
|
||||
url,
|
||||
checksum: {algorithm: 'sha256', value: checksum, source}
|
||||
});
|
||||
expect(spyHttpClient).toHaveBeenCalledTimes(1);
|
||||
expect(spyHttpClient).toHaveBeenCalledWith(
|
||||
source,
|
||||
expect.objectContaining({accept: 'application/vnd.github+json'})
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
it('rejects exact releases without an archive for the requested platform', async () => {
|
||||
spyHttpClient.mockResolvedValueOnce({
|
||||
statusCode: 200,
|
||||
headers: {},
|
||||
result: {
|
||||
body:
|
||||
'|[other archive](https://example.com/archive)|`' +
|
||||
'a'.repeat(64) +
|
||||
'`|'
|
||||
}
|
||||
});
|
||||
const distribution = new CorrettoDistribution({
|
||||
version: '25.0.5.0.10',
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: false
|
||||
});
|
||||
mockPlatform(distribution, 'linux');
|
||||
await expect(
|
||||
distribution['findPackageForDownload'](distribution['version'])
|
||||
).rejects.toThrow(
|
||||
'No linux/x64 jdk archive is listed for Corretto 25.0.5.0.10'
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects historical archives with only an MD5 checksum', async () => {
|
||||
spyHttpClient.mockResolvedValueOnce({
|
||||
statusCode: 200,
|
||||
headers: {},
|
||||
result: {
|
||||
body:
|
||||
'|[archive](https://corretto.aws/downloads/resources/17.0.1.12.1/amazon-corretto-17.0.1.12.1-linux-x64.tar.gz)|`' +
|
||||
'a'.repeat(32) +
|
||||
'`|'
|
||||
}
|
||||
});
|
||||
const distribution = new CorrettoDistribution({
|
||||
version: '17.0.1.12.1',
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: false
|
||||
});
|
||||
mockPlatform(distribution, 'linux');
|
||||
await expect(
|
||||
distribution['findPackageForDownload'](distribution['version'])
|
||||
).rejects.toThrow(
|
||||
'No SHA-256 checksum is published for Corretto 17.0.1.12.1 (linux, x64, jdk)'
|
||||
);
|
||||
});
|
||||
|
||||
it('does not substitute a JDK for an exact Linux JRE request', async () => {
|
||||
const distribution = new CorrettoDistribution({
|
||||
version: '25.0.5.0.10',
|
||||
architecture: 'x64',
|
||||
packageType: 'jre',
|
||||
checkLatest: false
|
||||
});
|
||||
mockPlatform(distribution, 'linux');
|
||||
await expect(
|
||||
distribution['findPackageForDownload'](distribution['version'])
|
||||
).rejects.toThrow('Corretto JRE archives are only available for Windows');
|
||||
expect(spyHttpClient).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('resolves catalog-only releases without a GitHub release', async () => {
|
||||
spyHttpClient.mockResolvedValueOnce({
|
||||
statusCode: 404,
|
||||
headers: {},
|
||||
result: null
|
||||
});
|
||||
const distribution = new CorrettoDistribution({
|
||||
version: '15.0.2.7.1',
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: false
|
||||
});
|
||||
mockPlatform(distribution, 'linux');
|
||||
expect(
|
||||
await distribution['findPackageForDownload'](distribution['version'])
|
||||
).toEqual({
|
||||
version: '15.0.2+7.1',
|
||||
url: 'https://corretto.aws/downloads/resources/15.0.2.7.1/amazon-corretto-15.0.2.7.1-linux-x64.tar.gz',
|
||||
checksum: {
|
||||
algorithm: 'sha256',
|
||||
value: manifestData.linux.x64.jdk['15']['tar.gz'].checksum_sha256,
|
||||
source:
|
||||
'https://corretto.github.io/corretto-downloads/latest_links/indexmap_with_checksum.json'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('falls back to the catalog when the GitHub lookup fails', async () => {
|
||||
spyHttpClient.mockRejectedValueOnce(new Error('GitHub rate limit'));
|
||||
const distribution = new CorrettoDistribution({
|
||||
version: '15.0.2.7.1',
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: false
|
||||
});
|
||||
mockPlatform(distribution, 'linux');
|
||||
|
||||
expect(
|
||||
await distribution['findPackageForDownload'](distribution['version'])
|
||||
).toEqual({
|
||||
version: '15.0.2+7.1',
|
||||
url: 'https://corretto.aws/downloads/resources/15.0.2.7.1/amazon-corretto-15.0.2.7.1-linux-x64.tar.gz',
|
||||
checksum: {
|
||||
algorithm: 'sha256',
|
||||
value: manifestData.linux.x64.jdk['15']['tar.gz'].checksum_sha256,
|
||||
source:
|
||||
'https://corretto.github.io/corretto-downloads/latest_links/indexmap_with_checksum.json'
|
||||
}
|
||||
});
|
||||
expect(spyHttpClient).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('reports missing release metadata without substituting the latest release', async () => {
|
||||
spyHttpClient.mockResolvedValueOnce({
|
||||
statusCode: 404,
|
||||
headers: {},
|
||||
result: null
|
||||
});
|
||||
const distribution = new CorrettoDistribution({
|
||||
version: '17.0.0.0.999',
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: false
|
||||
});
|
||||
mockPlatform(distribution, 'linux');
|
||||
await expect(
|
||||
distribution['findPackageForDownload'](distribution['version'])
|
||||
).rejects.toThrow(
|
||||
"Corretto release 17.0.0.0.999 was not found in Amazon's GitHub releases or current download catalog"
|
||||
);
|
||||
});
|
||||
|
||||
it('does not normalize zero-padded major-only inputs', () => {
|
||||
expect(
|
||||
() =>
|
||||
new CorrettoDistribution({
|
||||
version: '08',
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: false
|
||||
})
|
||||
).toThrow(
|
||||
"The string '08' is not valid SemVer notation for a Java version"
|
||||
);
|
||||
});
|
||||
|
||||
it('with unfound version throw could not find error', async () => {
|
||||
|
||||
@@ -82,7 +82,7 @@ describe('getJavaDistribution', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it.each(['8', '23.x', '23.0.1.1', '<24'])(
|
||||
it.each(['8', '23.x', '23.0.1.1', '23.0.1.1+1', '<24'])(
|
||||
"rejects Temurin java-package 'jdk+jmods' for version %s",
|
||||
async version => {
|
||||
await expect(
|
||||
@@ -96,7 +96,7 @@ describe('getJavaDistribution', () => {
|
||||
}
|
||||
);
|
||||
|
||||
it.each(['24', '24.0.1.1', '25-ea', '>=21', 'latest'])(
|
||||
it.each(['24', '24.0.1.1', '25.0.4.1+1', '25-ea', '>=21', 'latest'])(
|
||||
"accepts Temurin java-package 'jdk+jmods' for version %s",
|
||||
async version => {
|
||||
expect(
|
||||
|
||||
@@ -9,12 +9,14 @@ import {
|
||||
afterAll
|
||||
} from '@jest/globals';
|
||||
import https from 'https';
|
||||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import {HttpClient, HttpClientResponse} from '@actions/http-client';
|
||||
import type {IncomingMessage} from 'http';
|
||||
import {Readable} from 'stream';
|
||||
|
||||
import manifestData from '../data/jetbrains.json' with {type: 'json'};
|
||||
import os from 'os';
|
||||
|
||||
// Mock @actions/core before importing source modules that depend on it
|
||||
jest.unstable_mockModule('@actions/core', () => ({
|
||||
@@ -81,6 +83,7 @@ describe('getAvailableVersions', () => {
|
||||
jest.setTimeout(10_000);
|
||||
|
||||
let spyHttpClient: any;
|
||||
let spyHttpClientHead: any;
|
||||
let spyCoreError: any;
|
||||
const originalGitHubToken = process.env.GITHUB_TOKEN;
|
||||
|
||||
@@ -93,6 +96,10 @@ describe('getAvailableVersions', () => {
|
||||
headers: {},
|
||||
result: []
|
||||
});
|
||||
spyHttpClientHead = jest.spyOn(HttpClient.prototype, 'head');
|
||||
spyHttpClientHead.mockResolvedValue({
|
||||
message: {statusCode: 200}
|
||||
} as any);
|
||||
|
||||
// Mock core.error to suppress error logs
|
||||
spyCoreError = core.error as jest.Mock;
|
||||
@@ -133,9 +140,7 @@ describe('getAvailableVersions', () => {
|
||||
const availableVersions = await distribution['getAvailableVersions']();
|
||||
expect(availableVersions).not.toBeNull();
|
||||
|
||||
const length =
|
||||
os.platform() === 'win32' ? manifestData.length : manifestData.length + 2;
|
||||
expect(availableVersions.length).toBe(length);
|
||||
expect(availableVersions.length).toBe(manifestData.length + 2);
|
||||
}, 10_000);
|
||||
|
||||
it('continues a stable request after an all-prerelease page', async () => {
|
||||
@@ -358,6 +363,7 @@ describe('getAvailableVersions', () => {
|
||||
|
||||
it('retries a GitHub rate limit using Retry-After', async () => {
|
||||
spyHttpClient.mockRestore();
|
||||
spyHttpClientHead.mockRestore();
|
||||
const sleep = jest.fn(async () => undefined);
|
||||
const requestRaw = jest
|
||||
.spyOn(HttpClient.prototype, 'requestRaw')
|
||||
@@ -473,6 +479,109 @@ describe('findPackageForDownload', () => {
|
||||
}
|
||||
);
|
||||
|
||||
describe('hotfix releases', () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(HttpClient.prototype, 'getJson').mockResolvedValue({
|
||||
statusCode: 200,
|
||||
headers: {},
|
||||
result: [
|
||||
release('jbr-release-25.0.4b508.27', false),
|
||||
release('jbr-release-25.0.4.1b635.9', false),
|
||||
release('jbr-release-25.0.4.1b635.79', false),
|
||||
release('jbr-release-25.0.3.2b1000.1', false)
|
||||
]
|
||||
});
|
||||
jest.spyOn(HttpClient.prototype, 'head').mockResolvedValue(response(200));
|
||||
});
|
||||
|
||||
it.each(['linux', 'osx', 'windows'])(
|
||||
'selects the patched JBR archive and checksum on %s',
|
||||
async platform => {
|
||||
const distribution = new JetBrainsDistribution({
|
||||
version: '25',
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: true
|
||||
});
|
||||
distribution['getPlatformOption'] = () => platform;
|
||||
const url = `https://cache-redirector.jetbrains.com/intellij-jbr/jbrsdk-25.0.4.1-${platform}-x64-b635.79.tar.gz`;
|
||||
|
||||
const result = await distribution['findPackageForDownload']('25');
|
||||
|
||||
expect(result).toEqual({
|
||||
version: '25.0.4+1.635.79',
|
||||
url,
|
||||
checksum: {
|
||||
algorithm: 'sha512',
|
||||
value: JETBRAINS_CHECKSUM,
|
||||
source: `${url}.checksum`
|
||||
}
|
||||
});
|
||||
expect(HttpClient.prototype.head).toHaveBeenCalledWith(url);
|
||||
expect(spyHttpClientGet).toHaveBeenCalledTimes(1);
|
||||
expect(spyHttpClientGet).toHaveBeenCalledWith(`${url}.checksum`);
|
||||
}
|
||||
);
|
||||
|
||||
it.each([
|
||||
['latest', '25.0.4+1.635.79'],
|
||||
['25.0.4', '25.0.4+1.635.79'],
|
||||
['25.0.4.0', '25.0.4+508.27'],
|
||||
['25.0.4.1', '25.0.4+1.635.79'],
|
||||
['25.0.4.1+635.79', '25.0.4+1.635.79'],
|
||||
['25.0.4+1.635.9', '25.0.4+1.635.9'],
|
||||
['25.0.4+508.27', '25.0.4+508.27']
|
||||
])('resolves %s to %s', async (input, expected) => {
|
||||
const distribution = new JetBrainsDistribution({
|
||||
version: input,
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: true
|
||||
});
|
||||
|
||||
await expect(
|
||||
distribution['findPackageForDownload'](distribution['version'])
|
||||
).resolves.toMatchObject({version: expected});
|
||||
});
|
||||
|
||||
it('resolves early-access hotfix releases separately', async () => {
|
||||
jest.spyOn(HttpClient.prototype, 'getJson').mockResolvedValue({
|
||||
statusCode: 200,
|
||||
headers: {},
|
||||
result: [
|
||||
release('jbr-release-25.0.4.1b635.79', true),
|
||||
release('jbr-release-25.0.4.2b636.1', false)
|
||||
]
|
||||
});
|
||||
const distribution = new JetBrainsDistribution({
|
||||
version: '25.0.4.1-ea',
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: true
|
||||
});
|
||||
|
||||
await expect(
|
||||
distribution['findPackageForDownload'](distribution['version'])
|
||||
).resolves.toMatchObject({version: '25.0.4+1.635.79'});
|
||||
});
|
||||
|
||||
it.each(['25.0.4.2', '25.0.4.1+635.80'])(
|
||||
'does not substitute another patch or build for %s',
|
||||
async input => {
|
||||
const distribution = new JetBrainsDistribution({
|
||||
version: input,
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: true
|
||||
});
|
||||
|
||||
await expect(
|
||||
distribution['findPackageForDownload'](distribution['version'])
|
||||
).rejects.toThrow(/No matching version found/);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('version is not found', async () => {
|
||||
const distribution = new JetBrainsDistribution({
|
||||
version: '8.0.452',
|
||||
@@ -566,4 +675,84 @@ describe('findPackageForDownload', () => {
|
||||
source: `${result.url}.checksum`
|
||||
});
|
||||
});
|
||||
|
||||
describe('compareVersions', () => {
|
||||
it.each([
|
||||
['25.0.4+1.635.79', '25.0.4+508.27', 1],
|
||||
['25.0.5+1.1', '25.0.4+1.635.79', 1],
|
||||
['25.0.4+10.1.1', '25.0.4+2.999.99', 1],
|
||||
['25.0.4+1.635.79', '25.0.4+1.635.9', 1],
|
||||
['25.0.4+1.636.1', '25.0.4+1.635.79', 1],
|
||||
['25.0.4+0.635.79', '25.0.4+635.79', 0],
|
||||
['11.0.11+1522', '11.0.11+1522.1', -1],
|
||||
['25.0.4', '25.0.4+508.27', -1]
|
||||
])('compares %s with %s', (first, second, expected) => {
|
||||
const distribution = new JetBrainsDistribution({
|
||||
version: '25',
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: false
|
||||
});
|
||||
|
||||
expect(Math.sign(distribution['compareVersions'](first, second))).toBe(
|
||||
expected
|
||||
);
|
||||
expect(
|
||||
Math.sign(distribution['compareVersions'](second, first)) || 0
|
||||
).toBe(-expected || 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hotfix releases in the tool cache', () => {
|
||||
let cacheRoot: string;
|
||||
const previousToolcache = process.env['RUNNER_TOOL_CACHE'];
|
||||
|
||||
beforeEach(async () => {
|
||||
cacheRoot = await fs.promises.mkdtemp(
|
||||
path.join(os.tmpdir(), 'jetbrains-')
|
||||
);
|
||||
process.env['RUNNER_TOOL_CACHE'] = cacheRoot;
|
||||
for (const version of ['25.0.4-508.27', '25.0.4-1.635.79']) {
|
||||
const javaPath = path.join(
|
||||
cacheRoot,
|
||||
'Java_JetBrains_jdk',
|
||||
version,
|
||||
'x64'
|
||||
);
|
||||
await fs.promises.mkdir(javaPath, {recursive: true});
|
||||
await fs.promises.writeFile(`${javaPath}.complete`, '');
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
if (previousToolcache === undefined) {
|
||||
delete process.env['RUNNER_TOOL_CACHE'];
|
||||
} else {
|
||||
process.env['RUNNER_TOOL_CACHE'] = previousToolcache;
|
||||
}
|
||||
await fs.promises.rm(cacheRoot, {recursive: true, force: true});
|
||||
});
|
||||
|
||||
it.each(['25', '25.0.4.1', '25.0.4.1+635.79'])(
|
||||
'reuses the patched JBR for %s',
|
||||
input => {
|
||||
const distribution = new JetBrainsDistribution({
|
||||
version: input,
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: false
|
||||
});
|
||||
|
||||
expect(distribution['findInToolcache']()).toEqual({
|
||||
version: '25.0.4+1.635.79',
|
||||
path: path.join(
|
||||
cacheRoot,
|
||||
'Java_JetBrains_jdk',
|
||||
'25.0.4-1.635.79',
|
||||
'x64'
|
||||
)
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,6 +15,7 @@ import type {
|
||||
} from '../../src/distributions/liberica/models.js';
|
||||
import {HttpClient} from '@actions/http-client';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
|
||||
import manifestData from '../data/liberica.json' with {type: 'json'};
|
||||
|
||||
@@ -253,6 +254,126 @@ describe('findPackageForDownload', () => {
|
||||
expect(result.version).toBe(expected);
|
||||
});
|
||||
|
||||
describe.each([
|
||||
[17, 20],
|
||||
[25, 4]
|
||||
])('patch releases for Java %s.0.%s', (featureVersion, updateVersion) => {
|
||||
const mainVersion = `${featureVersion}.0.${updateVersion}`;
|
||||
|
||||
beforeEach(() => {
|
||||
distribution['getAvailableVersions'] = async () =>
|
||||
['+1', '+10', '.1+1', '.1+2', '.1+10', '.2+1', '.10+1'].map(suffix => ({
|
||||
featureVersion,
|
||||
interimVersion: 0,
|
||||
updateVersion,
|
||||
buildVersion: Number(suffix.split('+')[1]),
|
||||
version: `${mainVersion}${suffix}`,
|
||||
downloadUrl: `https://download.bell-sw.com/java/${mainVersion}${suffix}/bellsoft-jdk${mainVersion}${suffix}-macos-aarch64.tar.gz`
|
||||
}));
|
||||
});
|
||||
|
||||
it.each([
|
||||
[`${mainVersion}+1.1`, `${mainVersion}+1.1`, '.1+1'],
|
||||
[`${mainVersion}+1.2`, `${mainVersion}+1.2`, '.1+2'],
|
||||
[`${mainVersion}+1.10`, `${mainVersion}+1.10`, '.1+10'],
|
||||
[`${mainVersion}+1`, `${mainVersion}+1`, '+1'],
|
||||
[mainVersion, `${mainVersion}+10.1`, '.10+1'],
|
||||
[`${featureVersion}`, `${mainVersion}+10.1`, '.10+1']
|
||||
])('version is %s -> %s', async (input, expected, suffix) => {
|
||||
const result = await distribution['findPackageForDownload'](input);
|
||||
expect(result).toEqual({
|
||||
version: expected,
|
||||
url: `https://download.bell-sw.com/java/${mainVersion}${suffix}/bellsoft-jdk${mainVersion}${suffix}-macos-aarch64.tar.gz`
|
||||
});
|
||||
});
|
||||
|
||||
it('sorts builds within the same patch numerically', async () => {
|
||||
const versions = await distribution['getAvailableVersions']();
|
||||
distribution['getAvailableVersions'] = async () =>
|
||||
versions.filter(item => item.version.startsWith(`${mainVersion}.1+`));
|
||||
|
||||
const result = await distribution['findPackageForDownload'](mainVersion);
|
||||
expect(result.version).toBe(`${mainVersion}+1.10`);
|
||||
});
|
||||
|
||||
it('selects the newest build for a four-component patch-only request', async () => {
|
||||
const patchDistribution = new LibericaDistributions({
|
||||
version: `${mainVersion}.1`,
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: false
|
||||
});
|
||||
patchDistribution['getAvailableVersions'] =
|
||||
distribution['getAvailableVersions'];
|
||||
|
||||
await expect(
|
||||
patchDistribution['findPackageForDownload'](
|
||||
patchDistribution['version']
|
||||
)
|
||||
).resolves.toMatchObject({version: `${mainVersion}+1.10`});
|
||||
});
|
||||
|
||||
it('does not substitute a different compound build', async () => {
|
||||
await expect(
|
||||
distribution['findPackageForDownload'](`${mainVersion}+1.3`)
|
||||
).rejects.toThrow(/No matching version found for SemVer/);
|
||||
});
|
||||
});
|
||||
|
||||
it('selects the latest multi-component patch release for Java 25', async () => {
|
||||
const releases: LibericaVersion[] = [
|
||||
'25.0.5+11',
|
||||
'25.0.5.0.2+5',
|
||||
'25.0.5.0.3+5'
|
||||
].map(version => {
|
||||
const [versionWithoutBuild, buildVersion] = version.split('+');
|
||||
const [featureVersion, interimVersion, updateVersion] =
|
||||
versionWithoutBuild.split('.').map(Number);
|
||||
return {
|
||||
version,
|
||||
featureVersion,
|
||||
interimVersion,
|
||||
updateVersion,
|
||||
buildVersion: Number(buildVersion),
|
||||
downloadUrl: `https://download.bell-sw.com/java/${version}/jdk.tar.gz`
|
||||
};
|
||||
});
|
||||
distribution['getAvailableVersions'] = async () => releases;
|
||||
|
||||
await expect(distribution['findPackageForDownload']('25')).resolves.toEqual(
|
||||
{
|
||||
version: '25.0.5+0.3.5',
|
||||
url: 'https://download.bell-sw.com/java/25.0.5.0.3+5/jdk.tar.gz'
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('compares patch components before build numbers when patch lengths differ', async () => {
|
||||
const releases: LibericaVersion[] = ['25.0.5.1+10', '25.0.5.1.1+1'].map(
|
||||
version => {
|
||||
const [versionWithoutBuild, buildVersion] = version.split('+');
|
||||
const [featureVersion, interimVersion, updateVersion] =
|
||||
versionWithoutBuild.split('.').map(Number);
|
||||
return {
|
||||
version,
|
||||
featureVersion,
|
||||
interimVersion,
|
||||
updateVersion,
|
||||
buildVersion: Number(buildVersion),
|
||||
downloadUrl: `https://download.bell-sw.com/java/${version}/jdk.tar.gz`
|
||||
};
|
||||
}
|
||||
);
|
||||
distribution['getAvailableVersions'] = async () => releases;
|
||||
|
||||
await expect(
|
||||
distribution['findPackageForDownload']('25.0.5')
|
||||
).resolves.toEqual({
|
||||
version: '25.0.5+1.1.1',
|
||||
url: 'https://download.bell-sw.com/java/25.0.5.1.1+1/jdk.tar.gz'
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an error', async () => {
|
||||
await expect(distribution['findPackageForDownload']('17')).rejects.toThrow(
|
||||
/No matching version found for SemVer/
|
||||
@@ -260,6 +381,193 @@ describe('findPackageForDownload', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('patch request resolution from the constructor', () => {
|
||||
const releases: LibericaVersion[] = ['17.0.20+10', '17.0.20.1+1'].map(
|
||||
version => ({
|
||||
featureVersion: 17,
|
||||
interimVersion: 0,
|
||||
updateVersion: 20,
|
||||
buildVersion: Number(version.split('+')[1]),
|
||||
version,
|
||||
downloadUrl: `https://download.bell-sw.com/java/${version}/jdk.tar.gz`
|
||||
})
|
||||
);
|
||||
let cacheRoot: string;
|
||||
let previousToolcache: string | undefined;
|
||||
|
||||
beforeEach(async () => {
|
||||
previousToolcache = process.env['RUNNER_TOOL_CACHE'];
|
||||
cacheRoot = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'liberica-'));
|
||||
process.env['RUNNER_TOOL_CACHE'] = cacheRoot;
|
||||
for (const version of ['17.0.20-10', '17.0.20-1.1']) {
|
||||
const javaPath = path.join(
|
||||
cacheRoot,
|
||||
'Java_Liberica_jdk',
|
||||
version,
|
||||
'x64'
|
||||
);
|
||||
await fs.promises.mkdir(javaPath, {recursive: true});
|
||||
await fs.promises.writeFile(`${javaPath}.complete`, '');
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
if (previousToolcache === undefined) {
|
||||
delete process.env['RUNNER_TOOL_CACHE'];
|
||||
} else {
|
||||
process.env['RUNNER_TOOL_CACHE'] = previousToolcache;
|
||||
}
|
||||
await fs.promises.rm(cacheRoot, {recursive: true, force: true});
|
||||
});
|
||||
|
||||
it.each([
|
||||
['17', '17.0.20+1.1', '17.0.20.1+1'],
|
||||
['17.0.20', '17.0.20+1.1', '17.0.20.1+1'],
|
||||
['17.0.20+10', '17.0.20+10', '17.0.20+10'],
|
||||
['17.0.20+1.1', '17.0.20+1.1', '17.0.20.1+1'],
|
||||
['17.0.20.1', '17.0.20+1.1', '17.0.20.1+1'],
|
||||
['17.0.20.1+1', '17.0.20+1.1', '17.0.20.1+1'],
|
||||
['17.0.20.0', '17.0.20+10', '17.0.20+10']
|
||||
])(
|
||||
'resolves %s consistently remotely and in cache',
|
||||
async (input, expected, apiVersion) => {
|
||||
const distribution = new LibericaDistributions({
|
||||
version: input,
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: false
|
||||
});
|
||||
distribution['getAvailableVersions'] = async () => releases;
|
||||
|
||||
await expect(
|
||||
distribution['findPackageForDownload'](distribution['version'])
|
||||
).resolves.toEqual({
|
||||
version: expected,
|
||||
url: `https://download.bell-sw.com/java/${apiVersion}/jdk.tar.gz`
|
||||
});
|
||||
expect(distribution['findInToolcache']()).toEqual({
|
||||
version: expected,
|
||||
path: path.join(
|
||||
cacheRoot,
|
||||
'Java_Liberica_jdk',
|
||||
expected.replace('+', '-'),
|
||||
'x64'
|
||||
)
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
it.each(['17.0.20.2', '17.0.20+1.2'])(
|
||||
'does not substitute a different patch or exact build for %s',
|
||||
async version => {
|
||||
const distribution = new LibericaDistributions({
|
||||
version,
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: false
|
||||
});
|
||||
distribution['getAvailableVersions'] = async () => releases;
|
||||
|
||||
await expect(
|
||||
distribution['findPackageForDownload'](distribution['version'])
|
||||
).rejects.toThrow(/No matching version found/);
|
||||
expect(distribution['findInToolcache']()).toBeNull();
|
||||
}
|
||||
);
|
||||
|
||||
it('keeps patch-only and exact-build resolution cache identities distinct', () => {
|
||||
const distribution = new LibericaDistributions({
|
||||
version: '17.0.20.1',
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: false
|
||||
});
|
||||
expect(distribution['version']).toBe('17.0.20.1');
|
||||
});
|
||||
|
||||
it('normalizes multi-component patch-only requests for exact matching', async () => {
|
||||
const releases: LibericaVersion[] = ['25.0.5.0.2+5', '25.0.5.0.3+5'].map(
|
||||
version => {
|
||||
const [versionWithoutBuild, buildVersion] = version.split('+');
|
||||
const [featureVersion, interimVersion, updateVersion] =
|
||||
versionWithoutBuild.split('.').map(Number);
|
||||
return {
|
||||
version,
|
||||
featureVersion,
|
||||
interimVersion,
|
||||
updateVersion,
|
||||
buildVersion: Number(buildVersion),
|
||||
downloadUrl: `https://download.bell-sw.com/java/${version}/jdk.tar.gz`
|
||||
};
|
||||
}
|
||||
);
|
||||
const distribution = new LibericaDistributions({
|
||||
version: '25.0.5.0.3',
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: false
|
||||
});
|
||||
distribution['getAvailableVersions'] = async () => releases;
|
||||
|
||||
expect(distribution['version']).toBe('25.0.5.0.3');
|
||||
await expect(
|
||||
distribution['findPackageForDownload'](distribution['version'])
|
||||
).resolves.toEqual({
|
||||
version: '25.0.5+0.3.5',
|
||||
url: 'https://download.bell-sw.com/java/25.0.5.0.3+5/jdk.tar.gz'
|
||||
});
|
||||
});
|
||||
|
||||
it('orders main versions, patches, and builds consistently in cache', async () => {
|
||||
for (const version of ['17.0.20-1.2', '17.0.20-1.10', '17.0.21-1']) {
|
||||
const javaPath = path.join(
|
||||
cacheRoot,
|
||||
'Java_Liberica_jdk',
|
||||
version,
|
||||
'x64'
|
||||
);
|
||||
await fs.promises.mkdir(javaPath, {recursive: true});
|
||||
await fs.promises.writeFile(`${javaPath}.complete`, '');
|
||||
}
|
||||
|
||||
for (const [version, expected] of [
|
||||
['17', '17.0.21+1'],
|
||||
['17.0.20', '17.0.20+1.10'],
|
||||
['17.0.20.1', '17.0.20+1.10']
|
||||
]) {
|
||||
const distribution = new LibericaDistributions({
|
||||
version,
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: false
|
||||
});
|
||||
expect(distribution['findInToolcache']()?.version).toBe(expected);
|
||||
}
|
||||
});
|
||||
|
||||
it('preserves early-access handling for patch-only requests', async () => {
|
||||
const javaPath = path.join(
|
||||
cacheRoot,
|
||||
'Java_Liberica_jdk',
|
||||
'17.0.20-ea.1.2',
|
||||
'x64'
|
||||
);
|
||||
await fs.promises.mkdir(javaPath, {recursive: true});
|
||||
await fs.promises.writeFile(`${javaPath}.complete`, '');
|
||||
const distribution = new LibericaDistributions({
|
||||
version: '17.0.20.1-ea',
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: false
|
||||
});
|
||||
expect(distribution['stable']).toBe(false);
|
||||
expect(distribution['findInToolcache']()).toEqual({
|
||||
version: '17.0.20+1.2',
|
||||
path: javaPath
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getPlatformOption', () => {
|
||||
beforeEach(() => {
|
||||
// The linux row below is glibc, so pin the Alpine probe rather than
|
||||
@@ -335,6 +643,76 @@ describe('convertVersionToSemver', () => {
|
||||
buildVersion: 13
|
||||
},
|
||||
'11.0.0+13'
|
||||
],
|
||||
[
|
||||
{
|
||||
version: '17.0.20.1+1',
|
||||
featureVersion: 17,
|
||||
interimVersion: 0,
|
||||
updateVersion: 20,
|
||||
buildVersion: 1
|
||||
},
|
||||
'17.0.20+1.1'
|
||||
],
|
||||
[
|
||||
{
|
||||
version: '25.0.4.1+1',
|
||||
featureVersion: 25,
|
||||
interimVersion: 0,
|
||||
updateVersion: 4,
|
||||
buildVersion: 1
|
||||
},
|
||||
'25.0.4+1.1'
|
||||
],
|
||||
[
|
||||
{
|
||||
version: '25.0.5.0.3+5',
|
||||
featureVersion: 25,
|
||||
interimVersion: 0,
|
||||
updateVersion: 5,
|
||||
buildVersion: 5
|
||||
},
|
||||
'25.0.5+0.3.5'
|
||||
],
|
||||
[
|
||||
{
|
||||
version: '17.0.20.1+2',
|
||||
featureVersion: 17,
|
||||
interimVersion: 0,
|
||||
updateVersion: 20,
|
||||
buildVersion: 2
|
||||
},
|
||||
'17.0.20+1.2'
|
||||
],
|
||||
[
|
||||
{
|
||||
version: '25.0.4+1.1',
|
||||
featureVersion: 25,
|
||||
interimVersion: 0,
|
||||
updateVersion: 4,
|
||||
buildVersion: 1
|
||||
},
|
||||
'25.0.4+1.1'
|
||||
],
|
||||
[
|
||||
{
|
||||
version: '25+36',
|
||||
featureVersion: 25,
|
||||
interimVersion: 0,
|
||||
updateVersion: 0,
|
||||
buildVersion: 36
|
||||
},
|
||||
'25.0.0+36'
|
||||
],
|
||||
[
|
||||
{
|
||||
version: '8u202',
|
||||
featureVersion: 8,
|
||||
interimVersion: 0,
|
||||
updateVersion: 202,
|
||||
buildVersion: 8
|
||||
},
|
||||
'8.0.202+8'
|
||||
]
|
||||
])('%s -> %s', (input, expected) => {
|
||||
const actual = distributions['convertVersionToSemver']({
|
||||
|
||||
@@ -216,6 +216,172 @@ describe('findPackageForDownload', () => {
|
||||
expect(result.signatureUrl).toBe(`${url}.sig`);
|
||||
});
|
||||
|
||||
describe.each(['25.0.5.0.2', '25.0.5+0.2', '25.0.5'])(
|
||||
'version %s missing from the live manifest',
|
||||
version => {
|
||||
it.each([
|
||||
['darwin', 'x64', 'macos', 'tar.gz'],
|
||||
['linux', 'x64', 'linux', 'tar.gz'],
|
||||
['win32', 'x64', 'windows', 'zip'],
|
||||
['darwin', 'aarch64', 'macos', 'tar.gz'],
|
||||
['linux', 'aarch64', 'linux', 'tar.gz'],
|
||||
['win32', 'aarch64', 'windows', 'zip']
|
||||
])(
|
||||
'resolves the bundled release for %s/%s',
|
||||
async (platform, architecture, osType, extension) => {
|
||||
mockOsPlatform.mockReturnValue(platform);
|
||||
const distro = new MicrosoftDistributions({
|
||||
version,
|
||||
architecture,
|
||||
packageType: 'jdk',
|
||||
checkLatest: false
|
||||
});
|
||||
|
||||
const result = await distro['findPackageForDownload'](
|
||||
distro['version']
|
||||
);
|
||||
const url = `https://aka.ms/download-jdk/microsoft-jdk-25.0.5.0.2-${osType}-${architecture}.${extension}`;
|
||||
|
||||
expect(result).toEqual({
|
||||
version: '25.0.5+0.2',
|
||||
url,
|
||||
signatureUrl: `${url}.sig`,
|
||||
checksum: {
|
||||
algorithm: 'sha256',
|
||||
value: MICROSOFT_CHECKSUM,
|
||||
source: `${url}.sha256sum.txt`
|
||||
}
|
||||
});
|
||||
expect(spyHttpClientGet).toHaveBeenCalledWith(`${url}.sha256sum.txt`);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
it('prefers the live manifest when it contains the requested release', async () => {
|
||||
mockOsPlatform.mockReturnValue('linux');
|
||||
const url =
|
||||
'https://example.test/microsoft-jdk-25.0.5.0.2-linux-x64.tar.gz';
|
||||
spyGetManifestFromRepo.mockResolvedValue({
|
||||
result: [
|
||||
{
|
||||
version: '25.0.5+0.2',
|
||||
stable: true,
|
||||
release_url: 'https://aka.ms/download-jdk',
|
||||
files: [
|
||||
{
|
||||
filename: 'microsoft-jdk-25.0.5.0.2-linux-x64.tar.gz',
|
||||
arch: 'x64',
|
||||
platform: 'linux',
|
||||
download_url: url
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
statusCode: 200,
|
||||
headers: {}
|
||||
});
|
||||
|
||||
const result = await distribution['findPackageForDownload']('25.0.5+0.2');
|
||||
|
||||
expect(result.version).toBe('25.0.5+0.2');
|
||||
expect(result.url).toBe(url);
|
||||
expect(spyGetManifestFromRepo).toHaveBeenCalledWith(
|
||||
'https://aka.ms/download-jdk/microsoft-openjdk-versions.json',
|
||||
{accept: 'application/json'}
|
||||
);
|
||||
});
|
||||
|
||||
it('falls back to the bundled build when the live manifest has another build', async () => {
|
||||
mockOsPlatform.mockReturnValue('linux');
|
||||
spyGetManifestFromRepo.mockResolvedValue({
|
||||
result: [
|
||||
{
|
||||
version: '25.0.5+0.3',
|
||||
stable: true,
|
||||
release_url: 'https://aka.ms/download-jdk',
|
||||
files: [
|
||||
{
|
||||
filename: 'microsoft-jdk-25.0.5.0.3-linux-x64.tar.gz',
|
||||
arch: 'x64',
|
||||
platform: 'linux',
|
||||
download_url:
|
||||
'https://example.test/microsoft-jdk-25.0.5.0.3-linux-x64.tar.gz'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
statusCode: 200,
|
||||
headers: {}
|
||||
});
|
||||
|
||||
const result = await distribution['findPackageForDownload']('25.0.5+0.2');
|
||||
|
||||
expect(result.version).toBe('25.0.5+0.2');
|
||||
expect(result.url).toBe(
|
||||
'https://aka.ms/download-jdk/microsoft-jdk-25.0.5.0.2-linux-x64.tar.gz'
|
||||
);
|
||||
});
|
||||
|
||||
it('matches dotted Java versions without falling back to the base release', async () => {
|
||||
mockOsPlatform.mockReturnValue('linux');
|
||||
spyGetManifestFromRepo.mockResolvedValue({
|
||||
result: [
|
||||
{
|
||||
version: '17.0.8',
|
||||
stable: true,
|
||||
release_url: 'https://aka.ms/download-jdk',
|
||||
files: [
|
||||
{
|
||||
filename: 'microsoft-jdk-17.0.8-linux-x64.tar.gz',
|
||||
arch: 'x64',
|
||||
platform: 'linux',
|
||||
download_url: 'https://example.test/microsoft-jdk-17.0.8.tar.gz'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
version: '17.0.8.1',
|
||||
stable: true,
|
||||
release_url: 'https://aka.ms/download-jdk',
|
||||
files: [
|
||||
{
|
||||
filename: 'microsoft-jdk-17.0.8.1-linux-x64.tar.gz',
|
||||
arch: 'x64',
|
||||
platform: 'linux',
|
||||
download_url: 'https://example.test/microsoft-jdk-17.0.8.1.tar.gz'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
statusCode: 200,
|
||||
headers: {}
|
||||
});
|
||||
distribution = new MicrosoftDistributions({
|
||||
version: '17.0.8.1',
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: false
|
||||
});
|
||||
|
||||
const result = await distribution['findPackageForDownload'](
|
||||
distribution['version']
|
||||
);
|
||||
|
||||
expect(result.version).toBe('17.0.8+1');
|
||||
expect(result.url).toBe(
|
||||
'https://example.test/microsoft-jdk-17.0.8.1.tar.gz'
|
||||
);
|
||||
});
|
||||
|
||||
it('preserves the error when the live manifest cannot be loaded', async () => {
|
||||
spyGetManifestFromRepo.mockRejectedValue(new Error('Network error'));
|
||||
|
||||
await expect(
|
||||
distribution['findPackageForDownload']('25.0.5+0.2')
|
||||
).rejects.toThrow('Could not load manifest for Microsoft Build of OpenJDK');
|
||||
});
|
||||
|
||||
it.each([
|
||||
['amd64', 'x64'],
|
||||
['arm64', 'aarch64']
|
||||
|
||||
@@ -272,7 +272,7 @@ describe('findPackageForDownload', () => {
|
||||
}
|
||||
);
|
||||
|
||||
it.each(['zos', 'z/OS', 'z/os', 'test0987654321=', '++=++', 'myArch'])(
|
||||
it.each(['zos', 'myArch'])(
|
||||
'incorrect Semeru `%s` architecture throws',
|
||||
async (arch: string) => {
|
||||
const distribution = new SemeruDistribution({
|
||||
|
||||
@@ -293,7 +293,8 @@ describe('getAvailableVersions', () => {
|
||||
it.each([
|
||||
['amd64', 'x64'],
|
||||
['arm', 'arm'],
|
||||
['arm64', 'aarch64']
|
||||
['arm64', 'aarch64'],
|
||||
['riscv64', 'riscv64']
|
||||
])(
|
||||
'defaults to os.arch(): %s mapped to distro arch: %s',
|
||||
async (osArch: string, distroArch: string) => {
|
||||
@@ -377,6 +378,97 @@ describe('findPackageForDownload', () => {
|
||||
expect(resolvedVersion.version).toBe('16.0.2+7');
|
||||
});
|
||||
|
||||
describe('OpenJDK patch (respin) versions', () => {
|
||||
const makeRelease = (
|
||||
semverVersion: string,
|
||||
openjdkVersion: string,
|
||||
versionData: Record<string, number>
|
||||
) => ({
|
||||
binaries: [
|
||||
{
|
||||
package: {
|
||||
link: `https://example.com/${openjdkVersion}.tar.gz`,
|
||||
checksum: `checksum-${openjdkVersion}`,
|
||||
checksum_link: `https://example.com/${openjdkVersion}.sha256.txt`
|
||||
}
|
||||
}
|
||||
],
|
||||
version_data: {
|
||||
semver: semverVersion,
|
||||
openjdk_version: openjdkVersion,
|
||||
minor: 0,
|
||||
...versionData
|
||||
}
|
||||
});
|
||||
|
||||
const respinManifest = [
|
||||
makeRelease('26.0.2+101', '26.0.2.1+1', {
|
||||
major: 26,
|
||||
security: 2,
|
||||
patch: 1,
|
||||
build: 1
|
||||
}),
|
||||
makeRelease('26.0.2+10', '26.0.2+10', {
|
||||
major: 26,
|
||||
security: 2,
|
||||
build: 10
|
||||
}),
|
||||
makeRelease('25.0.4+101.0.LTS', '25.0.4.1+1-LTS', {
|
||||
major: 25,
|
||||
security: 4,
|
||||
patch: 1,
|
||||
build: 1
|
||||
}),
|
||||
makeRelease('25.0.4+7.0.LTS', '25.0.4+7-LTS', {
|
||||
major: 25,
|
||||
security: 4,
|
||||
build: 7
|
||||
})
|
||||
];
|
||||
|
||||
it.each([
|
||||
['26.0.2.1+1', '26.0.2+101'],
|
||||
['26.0.2+10', '26.0.2+10'],
|
||||
['26', '26.0.2+101'],
|
||||
['26.0.2', '26.0.2+101'],
|
||||
['25.0.4.1+1', '25.0.4+101.0.LTS'],
|
||||
['25.0.4+7', '25.0.4+7.0.LTS'],
|
||||
['25.0.4', '25.0.4+101.0.LTS']
|
||||
])('%s resolves to %s', async (input, expected) => {
|
||||
const distribution = new TemurinDistribution(
|
||||
{
|
||||
version: input,
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: false
|
||||
},
|
||||
TemurinImplementation.Hotspot
|
||||
);
|
||||
distribution['getAvailableVersions'] = async () => respinManifest as any;
|
||||
const resolvedVersion = await distribution['findPackageForDownload'](
|
||||
distribution['version']
|
||||
);
|
||||
expect(resolvedVersion.version).toBe(expected);
|
||||
expect(resolvedVersion).not.toHaveProperty('openjdkVersion');
|
||||
});
|
||||
|
||||
it('does not match a non-existent respin', async () => {
|
||||
const distribution = new TemurinDistribution(
|
||||
{
|
||||
version: '26.0.2.2+1',
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: false
|
||||
},
|
||||
TemurinImplementation.Hotspot
|
||||
);
|
||||
distribution['getAvailableVersions'] = async () => respinManifest as any;
|
||||
await expect(
|
||||
distribution['findPackageForDownload'](distribution['version'])
|
||||
).rejects.toThrow(/No matching version found for SemVer '26.0.2\+2.1'/);
|
||||
});
|
||||
});
|
||||
|
||||
it('version is found but binaries list is empty', async () => {
|
||||
const distribution = new TemurinDistribution(
|
||||
{
|
||||
|
||||
@@ -334,6 +334,69 @@ describe('findPackageForDownload', () => {
|
||||
);
|
||||
});
|
||||
|
||||
describe('hotfix builds with a 4-segment java_version', () => {
|
||||
// Mirrors the Azul Metadata API: 25.0.4.1 hotfix is reported as
|
||||
// java_version=[25,0,4,1], openjdk_build_number=1 (SDKMAN '25.0.4+1.1').
|
||||
const hotfixManifest = [
|
||||
{
|
||||
package_uuid: 'uuid-25.0.4+7',
|
||||
name: 'zulu25.36.15-ca-jdk25.0.4-linux_x64.tar.gz',
|
||||
download_url:
|
||||
'https://cdn.azul.com/zulu/bin/zulu25.36.15-ca-jdk25.0.4-linux_x64.tar.gz',
|
||||
java_version: [25, 0, 4],
|
||||
openjdk_build_number: 7,
|
||||
distro_version: [25, 36, 15, 0],
|
||||
latest: false,
|
||||
availability_type: 'ca'
|
||||
},
|
||||
{
|
||||
package_uuid: 'uuid-25.0.4+1.1',
|
||||
name: 'zulu25.36.205-ca-jdk25.0.4.1-linux_x64.tar.gz',
|
||||
download_url:
|
||||
'https://cdn.azul.com/zulu/bin/zulu25.36.205-ca-jdk25.0.4.1-linux_x64.tar.gz',
|
||||
java_version: [25, 0, 4, 1],
|
||||
openjdk_build_number: 1,
|
||||
distro_version: [25, 36, 205, 0],
|
||||
latest: true,
|
||||
availability_type: 'ca'
|
||||
},
|
||||
{
|
||||
package_uuid: 'uuid-25.0.3+9',
|
||||
name: 'zulu25.34.17-ca-jdk25.0.3-linux_x64.tar.gz',
|
||||
download_url:
|
||||
'https://cdn.azul.com/zulu/bin/zulu25.34.17-ca-jdk25.0.3-linux_x64.tar.gz',
|
||||
java_version: [25, 0, 3],
|
||||
openjdk_build_number: 9,
|
||||
distro_version: [25, 34, 17, 0],
|
||||
latest: false,
|
||||
availability_type: 'ca'
|
||||
}
|
||||
] as IZuluVersions[];
|
||||
|
||||
it.each([
|
||||
['25.0.4+1.1', '25.0.4+1.1', 'uuid-25.0.4+1.1'],
|
||||
['25', '25.0.4+1.1', 'uuid-25.0.4+1.1'],
|
||||
['25.0.4', '25.0.4+1.1', 'uuid-25.0.4+1.1'],
|
||||
['25.0.4+7', '25.0.4+7', 'uuid-25.0.4+7'],
|
||||
['25.0.3', '25.0.3+9', 'uuid-25.0.3+9']
|
||||
])('version is %s -> %s', async (input, expected, uuid) => {
|
||||
const distribution = new ZuluDistribution({
|
||||
version: input,
|
||||
architecture: 'x64',
|
||||
packageType: 'jdk',
|
||||
checkLatest: false
|
||||
});
|
||||
distribution['getAvailableVersions'] = async () => hotfixManifest;
|
||||
const result = await distribution['findPackageForDownload'](
|
||||
distribution['version']
|
||||
);
|
||||
expect(result.version).toBe(expected);
|
||||
expect(result.url).toBe(
|
||||
hotfixManifest.find(item => item.package_uuid === uuid)!.download_url
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an error', async () => {
|
||||
const distribution = new ZuluDistribution({
|
||||
version: '18',
|
||||
|
||||
+22
-5
@@ -236,7 +236,12 @@ describe('gpg tests', () => {
|
||||
process.env['RUNNER_TEMP'] = tempDir;
|
||||
});
|
||||
|
||||
it.each(['success', 'import failure', 'verification failure'])(
|
||||
it.each([
|
||||
'success',
|
||||
'import failure',
|
||||
'verification failure',
|
||||
'gpgconf unavailable'
|
||||
])(
|
||||
'uses a short macOS home or RUNNER_TEMP elsewhere and cleans up after %s',
|
||||
async outcome => {
|
||||
const longRunnerTemp = path.join(
|
||||
@@ -257,9 +262,16 @@ describe('gpg tests', () => {
|
||||
fs.writeFileSync(signaturePath, 'signature');
|
||||
(tc.downloadTool as jest.Mock<any>).mockResolvedValue(signaturePath);
|
||||
(exec.exec as jest.Mock<any>).mockImplementation(
|
||||
async (_command: string, args: string[]) => {
|
||||
async (command: string, args: string[]) => {
|
||||
gpgHome = path.join(expectedParent, path.posix.basename(args[1]));
|
||||
expect(args[1]).toBe(gpg.toGpgPath(gpgHome));
|
||||
if (command === 'gpgconf') {
|
||||
expect(fs.existsSync(gpgHome)).toBe(true);
|
||||
if (outcome === 'gpgconf unavailable') {
|
||||
throw new Error('gpgconf unavailable');
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (process.platform === 'darwin') {
|
||||
expect(
|
||||
Buffer.byteLength(path.join(gpgHome, 'S.gpg-agent.browser'))
|
||||
@@ -287,13 +299,18 @@ describe('gpg tests', () => {
|
||||
'https://example.com/jdk.tar.gz.sig',
|
||||
'public key'
|
||||
);
|
||||
if (outcome === 'success') {
|
||||
if (outcome === 'success' || outcome === 'gpgconf unavailable') {
|
||||
await verification;
|
||||
} else {
|
||||
await expect(verification).rejects.toThrow(outcome);
|
||||
}
|
||||
expect(exec.exec).toHaveBeenCalledTimes(
|
||||
outcome === 'import failure' ? 1 : 2
|
||||
outcome === 'import failure' ? 2 : 3
|
||||
);
|
||||
expect(exec.exec).toHaveBeenLastCalledWith(
|
||||
'gpgconf',
|
||||
['--homedir', gpg.toGpgPath(gpgHome), '--kill', 'gpg-agent'],
|
||||
{silent: true, ignoreReturnCode: true}
|
||||
);
|
||||
expect(fs.existsSync(gpgHome)).toBe(false);
|
||||
expect(fs.existsSync(signaturePath)).toBe(false);
|
||||
@@ -368,7 +385,7 @@ describe('gpg tests', () => {
|
||||
],
|
||||
expect.objectContaining({silent: true})
|
||||
);
|
||||
expect(exec.exec).toHaveBeenCalledTimes(2);
|
||||
expect(exec.exec).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -25,11 +25,42 @@ describe('Java platform capabilities', () => {
|
||||
['aarch64', 'aarch64'],
|
||||
['arm64', 'aarch64'],
|
||||
['ppc64le', 'ppc64le'],
|
||||
['RiScV64', 'riscv64'],
|
||||
['s390x', 's390x']
|
||||
])('normalizes architecture %s to %s', (input, expected) => {
|
||||
expect(normalizeArchitecture(input)).toBe(expected);
|
||||
});
|
||||
|
||||
it.each(['constructor', 'Constructor'])(
|
||||
'preserves custom architecture token %s for jdkfile',
|
||||
architecture => {
|
||||
expect(normalizeArchitecture(architecture)).toBe(architecture);
|
||||
expect(validateJavaPlatform('jdkfile', 'linux', architecture, '17')).toBe(
|
||||
architecture
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
it.each([
|
||||
'x64<<end\nvalue\nend',
|
||||
'z/OS',
|
||||
'z/os',
|
||||
'test0987654321=',
|
||||
'++=++',
|
||||
'../x64',
|
||||
'x64\\other',
|
||||
'x64 other',
|
||||
'-x64',
|
||||
'_x64'
|
||||
])('rejects invalid architecture token %s', architecture => {
|
||||
const message = `Unsupported architecture value: ${JSON.stringify(architecture)}`;
|
||||
|
||||
expect(() => normalizeArchitecture(architecture)).toThrow(message);
|
||||
expect(() =>
|
||||
validateJavaPlatform('jdkfile', 'linux', architecture, '17')
|
||||
).toThrow(message);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['linux', false, 'linux-glibc'],
|
||||
['linux', true, 'linux-musl'],
|
||||
@@ -68,6 +99,12 @@ describe('Java platform capabilities', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('allows Temurin on Linux riscv64', () => {
|
||||
expect(validateJavaPlatform('temurin', 'linux', 'riscv64', '25')).toBe(
|
||||
'riscv64'
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects OS-specific restrictions with a consistent diagnostic', () => {
|
||||
expect(() =>
|
||||
validateJavaPlatform('oracle', 'win32', 'arm64', '21')
|
||||
@@ -115,6 +152,7 @@ describe('Java platform capabilities', () => {
|
||||
'aarch64',
|
||||
'ppc64le',
|
||||
'ppc64',
|
||||
'riscv64',
|
||||
's390x'
|
||||
]) {
|
||||
expect(content).toContain(architecture);
|
||||
|
||||
@@ -27,7 +27,16 @@ describe('configureProblemMatcher', () => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('registers the Java problem matcher by default', () => {
|
||||
it('does not register the Java problem matcher by default', () => {
|
||||
configureProblemMatcher('/matchers/java.json');
|
||||
|
||||
expect(mockInfo).not.toHaveBeenCalled();
|
||||
expect(mockDebug).toHaveBeenCalledWith('Java problem matcher is disabled');
|
||||
});
|
||||
|
||||
it('registers the Java problem matcher when explicitly enabled', () => {
|
||||
inputs[INPUT_PROBLEM_MATCHER] = 'true';
|
||||
|
||||
configureProblemMatcher('/matchers/java.json');
|
||||
|
||||
expect(mockInfo).toHaveBeenCalledWith('##[add-matcher]/matchers/java.json');
|
||||
|
||||
+45
-1
@@ -43,6 +43,7 @@ const core = await import('@actions/core');
|
||||
|
||||
const {
|
||||
convertVersionToSemver,
|
||||
normalizeJavaVersionToSemver,
|
||||
getNextPageUrlFromLinkHeader,
|
||||
getVersionFromFileContent,
|
||||
isVersionSatisfies,
|
||||
@@ -188,6 +189,22 @@ describe('convertVersionToSemver', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('normalizeJavaVersionToSemver', () => {
|
||||
it.each([
|
||||
['17', '17'],
|
||||
['17.0.8', '17.0.8'],
|
||||
['17.0.8+7', '17.0.8+7'],
|
||||
['11.0.9.1', '11.0.9+1'],
|
||||
['12.0.2.1.0', '12.0.2+1.0'],
|
||||
['26.0.2.1+1', '26.0.2+1.1'],
|
||||
['17.0.8.1+1080.1', '17.0.8+1.1080.1'],
|
||||
['>=11.0.9.1', '>=11.0.9.1'],
|
||||
['17.x', '17.x']
|
||||
])('%s -> %s', (input: string, expected: string) => {
|
||||
expect(normalizeJavaVersionToSemver(input)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getNextPageUrlFromLinkHeader', () => {
|
||||
it.each([
|
||||
[
|
||||
@@ -261,6 +278,28 @@ describe('validatePaginationUrl', () => {
|
||||
});
|
||||
|
||||
describe('getVersionFromFileContent', () => {
|
||||
it.each(['25.0.5.0.10', '8.472.08.1', '25.0.5+0.10'])(
|
||||
'preserves full Corretto release %s in .java-version',
|
||||
version => {
|
||||
expect(
|
||||
getVersionFromFileContent(version, 'corretto', '.java-version')
|
||||
).toEqual({version, distribution: undefined});
|
||||
}
|
||||
);
|
||||
|
||||
it.each([
|
||||
['17.0.3.1', '.java-version', '17'],
|
||||
['1.8.0.472.1', '.java-version', '8'],
|
||||
['8.0.472.08.1', '.java-version', '8'],
|
||||
['8.0.282+8', '.java-version', '8'],
|
||||
['java corretto-17.0.13.11.1-jre', '.tool-versions', '17'],
|
||||
['java corretto-17.0.13.11.1+1', '.tool-versions', '17']
|
||||
])('keeps the existing major selection for %s', (content, file, version) => {
|
||||
expect(getVersionFromFileContent(content, 'corretto', file)?.version).toBe(
|
||||
version
|
||||
);
|
||||
});
|
||||
|
||||
describe('.sdkmanrc', () => {
|
||||
it.each([
|
||||
['java=26-tem', '26', 'temurin'],
|
||||
@@ -273,9 +312,13 @@ describe('getVersionFromFileContent', () => {
|
||||
['java=21.0.5-zulu', '21.0.5', 'zulu'],
|
||||
['java=17.0.13-albba', '17.0.13', 'dragonwell'],
|
||||
['java=17.0.13-amzn', '17', 'corretto'],
|
||||
['java=25.0.5.0.10-amzn', '25.0.5.0.10', 'corretto'],
|
||||
['java=8.472.08.1-amzn', '8.472.08.1', 'corretto'],
|
||||
['java=21.0.5-graal', '21.0.5', 'graalvm'],
|
||||
['java=17.0.9-graalce', '17.0.9', 'graalvm'],
|
||||
['java=11.0.25-librca', '11.0.25', 'liberica'],
|
||||
['java=25.0.4+1.1-librca', '25.0.4+1.1', 'liberica'],
|
||||
['java=25.0.4+1.1-zulu', '25.0.4+1.1', 'zulu'],
|
||||
['java=11.0.25-ms', '11.0.25', 'microsoft'],
|
||||
['java=21.0.5-oracle', '21.0.5', 'oracle'],
|
||||
['java=11.0.25-sapmchn', '11.0.25', 'sapmachine'],
|
||||
@@ -349,7 +392,8 @@ describe('getVersionFromFileContent', () => {
|
||||
['java adoptopenjdk-11.0.16+8', '11.0.16+8', 'temurin'],
|
||||
['java adoptopenjdk-openj9-11.0.16+8', '11.0.16+8', 'temurin'],
|
||||
['java zulu-11.56.19', '11.56.19', 'zulu'],
|
||||
['java corretto-17.0.13.11.1', '17', 'corretto'], // corretto -> major only
|
||||
['java corretto-17.0.13.11.1', '17.0.13.11.1', 'corretto'],
|
||||
['java corretto-8.472.08.1', '8.472.08.1', 'corretto'],
|
||||
['java liberica-11.0.15+10', '11.0.15+10', 'liberica'],
|
||||
['java microsoft-11.0.13.8.1', '11.0.13', 'microsoft'],
|
||||
['java semeru-openj9-11.0.25+9', '11.0.25+9', 'semeru'],
|
||||
|
||||
@@ -15,6 +15,13 @@ EXPECTED_PATH=$2
|
||||
SETUP_JAVA_VERSION=$3
|
||||
REQUIRE_CONCRETE_VERSION=$4
|
||||
|
||||
if [[ $EXPECTED_JAVA_VERSION == *"+"* ]] && [ "$SETUP_JAVA_VERSION" != "$EXPECTED_JAVA_VERSION" ]; then
|
||||
echo "::error::The version output does not match the requested build"
|
||||
echo "Requested version: $EXPECTED_JAVA_VERSION"
|
||||
echo "Version output: $SETUP_JAVA_VERSION"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
EXPECTED_JAVA_VERSION=$(echo $EXPECTED_JAVA_VERSION | cut -d'+' -f1)
|
||||
if [[ $EXPECTED_JAVA_VERSION == 8 ]] || [[ $EXPECTED_JAVA_VERSION == 8.* ]]; then
|
||||
EXPECTED_JAVA_VERSION="1.${EXPECTED_JAVA_VERSION}"
|
||||
|
||||
+14
-5
@@ -17,7 +17,7 @@ inputs:
|
||||
required: false
|
||||
default: 'jdk'
|
||||
architecture:
|
||||
description: "The architecture of the package (`x86`, `x64`, `armv7`, `aarch64`, `ppc64le`, `ppc64`, or `s390x`). Aliases `ia32`, `amd64`, `arm`, and `arm64` are normalized to `x86`, `x64`, `armv7`, and `aarch64`. Supported values vary by distribution and operating system. Defaults to the action runner's architecture."
|
||||
description: "The architecture of the package (`x86`, `x64`, `armv7`, `aarch64`, `ppc64le`, `ppc64`, `riscv64`, or `s390x`). Aliases `ia32`, `amd64`, `arm`, and `arm64` are normalized to `x86`, `x64`, `armv7`, and `aarch64`. Supported values vary by distribution and operating system. Defaults to the action runner's architecture."
|
||||
required: false
|
||||
jdk-file:
|
||||
description: 'Path to where the compressed JDK is located'
|
||||
@@ -66,15 +66,24 @@ inputs:
|
||||
mvn-server-credentials:
|
||||
description: 'Multiline list of Maven server credentials in the format `server-id:USERNAME_ENV:PASSWORD_ENV`. When set, replaces the single server configured by server-id, server-username-env-var, and server-password-env-var.'
|
||||
required: false
|
||||
mvn-server-repository-origins:
|
||||
description: 'Multiline list of Maven credential origins in the format `server-id:repository-origin`. Each origin must belong to a configured server ID. Requires Maven 3.10+.'
|
||||
required: false
|
||||
mvn-repositories:
|
||||
description: 'Multiline list of Maven dependency repositories in the format `repository-id:repository-url:snapshots-enabled`.'
|
||||
required: false
|
||||
mvn-plugin-repositories:
|
||||
description: 'Multiline list of Maven plugin repositories in the format `repository-id:repository-url:snapshots-enabled`.'
|
||||
required: false
|
||||
mvn-mirrors:
|
||||
description: 'Multiline list of Maven mirrors in the format `mirror-id:mirror-url:mirror-of`. The URL must include a scheme; percent-encode colons in its path, query, or fragment. Use a matching server ID for authentication. Can be used independently of repository inputs.'
|
||||
required: false
|
||||
mvn-repositories-include-central:
|
||||
description: 'Include Maven Central in the generated dependency repositories profile. When false, Central is disabled unless an explicit repository with ID `central` is declared.'
|
||||
description: 'Include Maven Central for dependency and plugin repositories in the generated profile. When false, Central is disabled for both types unless that type explicitly declares ID `central`. Has no effect when neither repository list is configured.'
|
||||
required: false
|
||||
default: true
|
||||
mvn-repositories-prioritize-central:
|
||||
description: 'Place Maven Central before custom dependency repositories. Has no effect when Maven Central is excluded.'
|
||||
description: 'Place Maven Central before custom dependency and plugin repositories. Has no effect when Maven Central is excluded or neither repository list is configured.'
|
||||
required: false
|
||||
default: true
|
||||
settings-path:
|
||||
@@ -129,9 +138,9 @@ inputs:
|
||||
required: false
|
||||
default: false
|
||||
problem-matcher:
|
||||
description: 'Whether to register the Java problem matcher (compiler errors/warnings and uncaught exceptions). Set to "false" to disable annotations.'
|
||||
description: 'Whether to register the Java problem matcher (compiler errors/warnings and uncaught exceptions). Disabled by default; set to "true" to enable annotations.'
|
||||
required: false
|
||||
default: true
|
||||
default: false
|
||||
outputs:
|
||||
distribution:
|
||||
description: 'Distribution of Java that has been installed'
|
||||
|
||||
Vendored
+63446
File diff suppressed because it is too large
Load Diff
Vendored
+2
-2
@@ -21,8 +21,8 @@ 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/cache/lib/cache.js + 323 modules
|
||||
var lib_cache = __webpack_require__(8230);
|
||||
// EXTERNAL MODULE: ./node_modules/@actions/core/lib/core.js + 7 modules
|
||||
var lib_core = __webpack_require__(3838);
|
||||
// EXTERNAL MODULE: ./src/util.ts
|
||||
|
||||
Vendored
+1
-1
@@ -15,7 +15,7 @@ export const modules = {
|
||||
/* 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__(5767);
|
||||
/* harmony import */ var _actions_cache__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8230);
|
||||
/* harmony import */ var _actions_core__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(3838);
|
||||
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -13,7 +13,7 @@ export const modules = {
|
||||
/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__);
|
||||
/* harmony import */ var os__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(857);
|
||||
/* harmony import */ var os__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(os__WEBPACK_IMPORTED_MODULE_1__);
|
||||
/* harmony import */ var _actions_cache__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5767);
|
||||
/* harmony import */ var _actions_cache__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(8230);
|
||||
/* harmony import */ var _actions_core__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(3838);
|
||||
/* harmony import */ var _actions_glob__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(2377);
|
||||
/**
|
||||
|
||||
Vendored
+158
-22
@@ -18360,9 +18360,102 @@ function readAttributeStr(xmlData, i) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Select all the attributes whether valid or invalid.
|
||||
* Walk `attrStr` once, left to right, splitting it into attribute tokens.
|
||||
*
|
||||
* This replaces a regex that used to do the same job
|
||||
* (`(\s*)([^\s=]+)(\s*=)?(\s*(['"])(([\s\S])*?)\5)?`). That regex led with an
|
||||
* optional whitespace group followed by a required "non-whitespace" group.
|
||||
* On a long run of whitespace that never resolves into an attribute name
|
||||
* (e.g. a tag with thousands of trailing spaces before `>`), the engine
|
||||
* backtracks the whitespace group one character at a time before giving up
|
||||
* and moving to the next starting position — one full backtrack per
|
||||
* position, which is quadratic in the length of the run.
|
||||
*
|
||||
* A single forward-only scan can never backtrack, so it can't be made slow
|
||||
* this way no matter how much whitespace the input contains — it's always
|
||||
* proportional to the length of the string, once.
|
||||
*
|
||||
* Each returned token mirrors the shape the old regex match array had, so
|
||||
* the validation logic below (which reads token[1]..token[6]) didn't need
|
||||
* to change:
|
||||
* token.startIndex - where this token begins in attrStr
|
||||
* token[1] - leading whitespace before the name
|
||||
* token[2] - the attribute name
|
||||
* token[3] - whitespace + '=' if present, else undefined
|
||||
* token[4] - marker (any defined value) if a quoted value was found
|
||||
* token[5] - the quote character used ('"' or "'")
|
||||
* token[6] - the value's text, without the surrounding quotes
|
||||
*
|
||||
* A malformed leading character (e.g. a stray '=' with no name before it)
|
||||
* is simply skipped over, one character at a time — the same outcome the
|
||||
* old regex produced by failing to match at that position and retrying at
|
||||
* the next one.
|
||||
*/
|
||||
const validAttrStrRegxp = new RegExp('(\\s*)([^\\s=]+)(\\s*=)?(\\s*([\'"])(([\\s\\S])*?)\\5)?', 'g');
|
||||
function scanAttributeTokens(attrStr) {
|
||||
const tokens = [];
|
||||
const len = attrStr.length;
|
||||
let i = 0;
|
||||
|
||||
while (i < len) {
|
||||
const tokenStart = i;
|
||||
|
||||
// Leading whitespace before the name.
|
||||
while (i < len && isWhiteSpace(attrStr[i])) i++;
|
||||
if (i >= len) break; // trailing whitespace only — nothing left to read
|
||||
|
||||
if (attrStr[i] === '=') {
|
||||
// No name before this '=' — not a valid attribute start. Move past
|
||||
// just this one character and try again from the next position.
|
||||
i = tokenStart + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
const leadingWs = attrStr.slice(tokenStart, i);
|
||||
|
||||
// Attribute name — everything up to the next whitespace or '='.
|
||||
const nameStart = i;
|
||||
while (i < len && !isWhiteSpace(attrStr[i]) && attrStr[i] !== '=') i++;
|
||||
const name = attrStr.slice(nameStart, i);
|
||||
|
||||
// Optional whitespace + '='.
|
||||
let equalsGroup; // whitespace + '=' text, or undefined if absent
|
||||
let j = i;
|
||||
while (j < len && isWhiteSpace(attrStr[j])) j++;
|
||||
if (j < len && attrStr[j] === '=') {
|
||||
equalsGroup = attrStr.slice(i, j + 1);
|
||||
i = j + 1;
|
||||
}
|
||||
|
||||
// Optional whitespace + quoted value.
|
||||
let quoteChar;
|
||||
let value;
|
||||
let k = i;
|
||||
while (k < len && isWhiteSpace(attrStr[k])) k++;
|
||||
if (k < len && (attrStr[k] === '"' || attrStr[k] === "'")) {
|
||||
const valueStart = k + 1;
|
||||
const closeIdx = attrStr.indexOf(attrStr[k], valueStart);
|
||||
if (closeIdx !== -1) {
|
||||
quoteChar = attrStr[k];
|
||||
value = attrStr.slice(valueStart, closeIdx);
|
||||
i = closeIdx + 1;
|
||||
}
|
||||
// No closing quote found anywhere in the rest of the string — leave
|
||||
// quoteChar/value undefined, same as the old regex's group failing
|
||||
// to match a backreference-less run.
|
||||
}
|
||||
|
||||
const token = { startIndex: tokenStart };
|
||||
token[1] = leadingWs;
|
||||
token[2] = name;
|
||||
token[3] = equalsGroup;
|
||||
token[4] = quoteChar !== undefined ? true : undefined;
|
||||
token[5] = quoteChar;
|
||||
token[6] = value;
|
||||
tokens.push(token);
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
//attr, ="sd", a="amit's", a="sd"b="saf", ab cd=""
|
||||
|
||||
@@ -18371,7 +18464,7 @@ function validateAttributeString(attrStr, options) {
|
||||
|
||||
//if(attrStr.trim().length === 0) return true; //empty string
|
||||
|
||||
const matches = getAllMatches(attrStr, validAttrStrRegxp);
|
||||
const matches = scanAttributeTokens(attrStr);
|
||||
const attrNames = {};
|
||||
|
||||
for (let i = 0; i < matches.length; i++) {
|
||||
@@ -18473,7 +18566,6 @@ function getLineNumberForPosition(xmlData, index) {
|
||||
function getPositionFromMatch(match) {
|
||||
return match.startIndex + match[1].length;
|
||||
}
|
||||
|
||||
;// CONCATENATED MODULE: ./node_modules/fast-xml-parser/src/fxp.js
|
||||
|
||||
|
||||
@@ -59498,6 +59590,24 @@ const EXPANSION_MAX = 100_000;
|
||||
// realistic expansion (100k results hitting `EXPANSION_MAX` measure ~1M
|
||||
// characters) so legitimate input is unaffected.
|
||||
const EXPANSION_MAX_LENGTH = 4_000_000;
|
||||
// `expand_` recurses once per level of brace *nesting* - both when expanding a
|
||||
// set's comma members and when re-wrapping a set whose body is a single part.
|
||||
// The CVE-2026-14257 fix made the *tail* iterative (recursion on `m.post`, one
|
||||
// level per chained group), which left nesting depth unbounded: about 3,100
|
||||
// levels of `{{{...a,b...}}}` - only ~6KB of input - exhausted the native stack
|
||||
// and crashed the process. `EXPANSION_MAX_DEPTH` bounds how deep the parser
|
||||
// will follow nesting. It sits far above any realistic pattern and well below
|
||||
// the depth at which the stack runs out.
|
||||
const EXPANSION_MAX_DEPTH = 1_000;
|
||||
// Bash keeps a quirk where a brace group followed by a comma set still expands
|
||||
// (`{a},b}`). The parser implements it by rewriting the string and restarting
|
||||
// the scan, absorbing one `}` per pass. `n` trailing braces therefore cost `n`
|
||||
// full passes over a string that itself grows by one `escClose` sentinel each
|
||||
// time - quadratic in `n`, with a ~26x constant from the sentinel's length.
|
||||
// 128KB of `'{a}' + '}'.repeat(n) + ',z}'` blocked the event loop for 27
|
||||
// seconds to produce two results. `EXPANSION_MAX_REWRITES` bounds how many
|
||||
// times the scan may restart. Real `{a},b}` input needs a handful.
|
||||
const EXPANSION_MAX_REWRITES = 1_000;
|
||||
function numeric(str) {
|
||||
return !isNaN(str) ? parseInt(str, 10) : str.charCodeAt(0);
|
||||
}
|
||||
@@ -59517,37 +59627,52 @@ function unescapeBraces(str) {
|
||||
.replace(escCommaPattern, ',')
|
||||
.replace(escPeriodPattern, '.');
|
||||
}
|
||||
// Like `target.push(...items)` but doesn't overflow the stack
|
||||
function pushAll(target, items) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
target.push(items[i]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Basically just str.split(","), but handling cases
|
||||
* where we have nested braced sections, which should be
|
||||
* treated as individual members, like {a,{b,c},d}
|
||||
*/
|
||||
function parseCommaParts(str) {
|
||||
if (!str) {
|
||||
return [''];
|
||||
}
|
||||
const parts = [];
|
||||
// Walk the brace groups iteratively. Recursing on `post` once per group let a
|
||||
// chain of them exhaust the stack - the parsing-side counterpart to
|
||||
// the `expand_` overflow fixed for CVE-2026-14257, and not something `max` or
|
||||
// `maxLength` can bound, since it happens before expansion.
|
||||
//
|
||||
// The part the next chunk continues
|
||||
let carry = '';
|
||||
for (;;) {
|
||||
const m = balanced('{', '}', str);
|
||||
if (!m) {
|
||||
return str.split(',');
|
||||
const tail = str.split(',');
|
||||
tail[0] = carry + tail[0];
|
||||
pushAll(parts, tail);
|
||||
return parts;
|
||||
}
|
||||
const { pre, body, post } = m;
|
||||
const p = pre.split(',');
|
||||
p[0] = carry + p[0];
|
||||
p[p.length - 1] += '{' + body + '}';
|
||||
const postParts = parseCommaParts(post);
|
||||
if (post.length) {
|
||||
;
|
||||
p[p.length - 1] += postParts.shift();
|
||||
p.push.apply(p, postParts);
|
||||
}
|
||||
parts.push.apply(parts, p);
|
||||
if (!post.length) {
|
||||
pushAll(parts, p);
|
||||
return parts;
|
||||
}
|
||||
carry = p.pop();
|
||||
pushAll(parts, p);
|
||||
str = post;
|
||||
}
|
||||
}
|
||||
function expand(str, options = {}) {
|
||||
if (!str) {
|
||||
return [];
|
||||
}
|
||||
const { max = EXPANSION_MAX, maxLength = EXPANSION_MAX_LENGTH } = options;
|
||||
const { max = EXPANSION_MAX, maxLength = EXPANSION_MAX_LENGTH, maxDepth = EXPANSION_MAX_DEPTH, maxRewrites = EXPANSION_MAX_REWRITES, } = options;
|
||||
// I don't know why Bash 4.3 does this, but it does.
|
||||
// Anything starting with {} will have the first two bytes preserved
|
||||
// but *only* at the top level, so {},a}b will not expand to anything,
|
||||
@@ -59557,7 +59682,7 @@ function expand(str, options = {}) {
|
||||
if (str.slice(0, 2) === '{}') {
|
||||
str = '\\{\\}' + str.slice(2);
|
||||
}
|
||||
return expand_(escapeBraces(str), max, maxLength, true).map(unescapeBraces);
|
||||
return expand_(escapeBraces(str), max, maxLength, maxDepth, 0, maxRewrites, true).map(unescapeBraces);
|
||||
}
|
||||
function embrace(str) {
|
||||
return '{' + str + '}';
|
||||
@@ -59652,7 +59777,13 @@ function expandSequence(body, isAlphaSequence, max, maxLength) {
|
||||
}
|
||||
return N;
|
||||
}
|
||||
function expand_(str, max, maxLength, isTop) {
|
||||
function expand_(str, max, maxLength, maxDepth, depth, maxRewrites, isTop) {
|
||||
// Too deeply nested to keep following: treat the rest as literal, the same
|
||||
// way a group that cannot expand is already handled. Truncating rather than
|
||||
// throwing keeps `expand` total, matching `max` and `maxLength`.
|
||||
if (depth > maxDepth) {
|
||||
return [str];
|
||||
}
|
||||
// Consume the string's top-level brace groups left to right, threading a
|
||||
// running set of combined prefixes (`acc`). Expanding the tail iteratively -
|
||||
// rather than recursing on `m.post` once per group - keeps the native stack
|
||||
@@ -59664,6 +59795,9 @@ function expand_(str, max, maxLength, isTop) {
|
||||
// comma set - a sequence like `{a..\}` may legitimately yield ''. The drop
|
||||
// is on the final strings, so it is applied to whichever `combine` produces
|
||||
// them (the one with no brace set left in the tail).
|
||||
// How many times the `{a},b}` rewrite below has restarted the scan. Each pass
|
||||
// re-reads the whole string, so leaving this unbounded is quadratic.
|
||||
let rewrites = 0;
|
||||
let dropEmpties = false;
|
||||
let firstGroup = true;
|
||||
for (;;) {
|
||||
@@ -59688,7 +59822,8 @@ function expand_(str, max, maxLength, isTop) {
|
||||
const isOptions = m.body.indexOf(',') >= 0;
|
||||
if (!isSequence && !isOptions) {
|
||||
// {a},b}
|
||||
if (m.post.match(/,(?!,).*\}/)) {
|
||||
if (rewrites < maxRewrites && m.post.match(/,(?!,).*\}/)) {
|
||||
rewrites++;
|
||||
str = m.pre + '{' + m.body + escClose + m.post;
|
||||
isTop = true;
|
||||
continue;
|
||||
@@ -59708,7 +59843,7 @@ function expand_(str, max, maxLength, isTop) {
|
||||
let n = parseCommaParts(m.body);
|
||||
if (n.length === 1 && n[0] !== undefined) {
|
||||
// x{{a,b}}y ==> x{a}y x{b}y
|
||||
n = expand_(n[0], max, maxLength, false).map(embrace);
|
||||
n = expand_(n[0], max, maxLength, maxDepth, depth + 1, maxRewrites, false).map(embrace);
|
||||
//XXX is this necessary? Can't seem to hit it in tests.
|
||||
/* c8 ignore start */
|
||||
if (n.length === 1) {
|
||||
@@ -59734,12 +59869,13 @@ function expand_(str, max, maxLength, isTop) {
|
||||
values = [];
|
||||
let valuesLength = 0;
|
||||
outer: for (let j = 0; j < n.length; j++) {
|
||||
const expanded = expand_(n[j], max, maxLength, false);
|
||||
const expanded = expand_(n[j], max, maxLength, maxDepth, depth + 1, maxRewrites, false);
|
||||
for (let k = 0; k < expanded.length; k++) {
|
||||
const v = expanded[k];
|
||||
if (dropsEmpties && !v)
|
||||
continue;
|
||||
if (values.length >= max || valuesLength + v.length > maxLength) {
|
||||
if (values.length >= max ||
|
||||
valuesLength + v.length > maxLength) {
|
||||
break outer;
|
||||
}
|
||||
values.push(v);
|
||||
|
||||
Vendored
+63317
File diff suppressed because it is too large
Load Diff
Vendored
+609
-219
File diff suppressed because it is too large
Load Diff
Vendored
+98
-13
@@ -13,9 +13,11 @@ export const modules = {
|
||||
/* 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 _util_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4527);
|
||||
/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(6242);
|
||||
/* harmony import */ var _platform_types_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(7444);
|
||||
/* harmony import */ var _actions_http_client__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4942);
|
||||
/* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(4527);
|
||||
/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(6242);
|
||||
/* harmony import */ var _platform_types_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(7444);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -23,29 +25,42 @@ export const modules = {
|
||||
|
||||
|
||||
const CORRETTO_VERSIONS_URL = 'https://corretto.github.io/corretto-downloads/latest_links/indexmap_with_checksum.json';
|
||||
class CorrettoDistribution extends _base_installer_js__WEBPACK_IMPORTED_MODULE_4__/* .JavaBase */ .O {
|
||||
class CorrettoDistribution extends _base_installer_js__WEBPACK_IMPORTED_MODULE_5__/* .JavaBase */ .O {
|
||||
requestedVersion;
|
||||
constructor(installerOptions) {
|
||||
super('Corretto', installerOptions);
|
||||
super('Corretto', {
|
||||
...installerOptions,
|
||||
// Java 8 releases use zero-padded fields, e.g. 8.472.08.1.
|
||||
version: (0,_util_js__WEBPACK_IMPORTED_MODULE_4__/* .isExactCorrettoVersion */ .kI)(installerOptions.version)
|
||||
? installerOptions.version.replace(/(^|\.)0+(?=\d)/g, '$1')
|
||||
: installerOptions.version
|
||||
});
|
||||
this.requestedVersion = installerOptions.version
|
||||
.replace('+', '.')
|
||||
.replace(/^8\.(\d+)\.(\d)\./, '8.$1.0$2.');
|
||||
}
|
||||
async downloadTool(javaRelease) {
|
||||
_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .info */ .pq(`Downloading Java ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...`);
|
||||
let javaArchivePath = await this.downloadAndVerify(javaRelease);
|
||||
_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .info */ .pq(`Extracting Java archive...`);
|
||||
const extension = (0,_util_js__WEBPACK_IMPORTED_MODULE_3__/* .getDownloadArchiveExtension */ .ag)();
|
||||
const extension = (0,_util_js__WEBPACK_IMPORTED_MODULE_4__/* .getDownloadArchiveExtension */ .ag)();
|
||||
if (process.platform === 'win32') {
|
||||
javaArchivePath = (0,_util_js__WEBPACK_IMPORTED_MODULE_3__/* .renameWinArchive */ .n2)(javaArchivePath);
|
||||
javaArchivePath = (0,_util_js__WEBPACK_IMPORTED_MODULE_4__/* .renameWinArchive */ .n2)(javaArchivePath);
|
||||
}
|
||||
const extractedJavaPath = await (0,_util_js__WEBPACK_IMPORTED_MODULE_3__/* .extractJdkFile */ .PE)(javaArchivePath, extension);
|
||||
const extractedJavaPath = await (0,_util_js__WEBPACK_IMPORTED_MODULE_4__/* .extractJdkFile */ .PE)(javaArchivePath, extension);
|
||||
const archiveName = fs__WEBPACK_IMPORTED_MODULE_1___default().readdirSync(extractedJavaPath)[0];
|
||||
const archivePath = path__WEBPACK_IMPORTED_MODULE_2___default().join(extractedJavaPath, archiveName);
|
||||
const version = this.getToolcacheVersionName(javaRelease.version);
|
||||
const javaPath = await (0,_util_js__WEBPACK_IMPORTED_MODULE_3__/* .cacheJdkDir */ .Vj)(archivePath, this.toolcacheFolderName, version, this.architecture);
|
||||
const javaPath = await (0,_util_js__WEBPACK_IMPORTED_MODULE_4__/* .cacheJdkDir */ .Vj)(archivePath, this.toolcacheFolderName, version, this.architecture);
|
||||
return { version: javaRelease.version, path: javaPath };
|
||||
}
|
||||
async findPackageForDownload(version) {
|
||||
if (!this.stable) {
|
||||
throw new Error('Early access versions are not supported');
|
||||
}
|
||||
if ((0,_util_js__WEBPACK_IMPORTED_MODULE_4__/* .isExactCorrettoVersion */ .kI)(this.requestedVersion)) {
|
||||
return this.findExactRelease();
|
||||
}
|
||||
const availableVersions = await this.getAvailableVersions();
|
||||
// The `latest` alias is normalized to the SemVer wildcard, but Corretto
|
||||
// matches on an exact major version, so resolve it to the newest available
|
||||
@@ -60,13 +75,13 @@ class CorrettoDistribution extends _base_installer_js__WEBPACK_IMPORTED_MODULE_4
|
||||
version = Math.max(...majors).toString();
|
||||
}
|
||||
if (version.includes('.')) {
|
||||
throw new Error('Only major versions are supported');
|
||||
throw new Error('Specify a major version or a full Corretto release, such as 25.0.5.0.10');
|
||||
}
|
||||
const matchingVersions = availableVersions
|
||||
.filter(item => item.version == version)
|
||||
.map(item => {
|
||||
return {
|
||||
version: (0,_util_js__WEBPACK_IMPORTED_MODULE_3__/* .convertVersionToSemver */ .ZY)(item.correttoVersion),
|
||||
version: (0,_util_js__WEBPACK_IMPORTED_MODULE_4__/* .convertVersionToSemver */ .ZY)(item.correttoVersion),
|
||||
url: item.downloadLink,
|
||||
checksum: {
|
||||
algorithm: 'sha256',
|
||||
@@ -82,6 +97,76 @@ class CorrettoDistribution extends _base_installer_js__WEBPACK_IMPORTED_MODULE_4
|
||||
}
|
||||
return resolvedVersion;
|
||||
}
|
||||
async findExactRelease() {
|
||||
const version = this.requestedVersion;
|
||||
const major = version.split('.')[0];
|
||||
const platform = this.getPlatformOption();
|
||||
const arch = this.architecture;
|
||||
if (platform !== 'windows' && this.packageType !== 'jdk') {
|
||||
throw new Error('Corretto JRE archives are only available for Windows');
|
||||
}
|
||||
const os = platform === 'macos'
|
||||
? 'macosx'
|
||||
: platform === 'alpine'
|
||||
? 'alpine-linux'
|
||||
: platform;
|
||||
const suffix = platform === 'windows' ? `-${this.packageType}.zip` : '.tar.gz';
|
||||
const url = `https://corretto.aws/downloads/resources/${version}/amazon-corretto-${version}-${os}-${arch}${suffix}`;
|
||||
// Versioned archives have no checksum sidecar; Amazon publishes their SHA-256s in the release notes.
|
||||
const source = `https://api.github.com/repos/corretto/corretto-${major}/releases/tags/${version}`;
|
||||
const findCatalogRelease = async () => {
|
||||
const archive = (await this.getAvailableVersions()).find(item => item.downloadLink === url);
|
||||
if (!archive) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
version: this.version,
|
||||
url,
|
||||
checksum: {
|
||||
algorithm: 'sha256',
|
||||
value: archive.checksum_sha256,
|
||||
source: CORRETTO_VERSIONS_URL
|
||||
}
|
||||
};
|
||||
};
|
||||
let response;
|
||||
try {
|
||||
response = await this.http.getJson(source, {
|
||||
...(0,_util_js__WEBPACK_IMPORTED_MODULE_4__/* .getGitHubHttpHeaders */ .U_)(),
|
||||
accept: 'application/vnd.github+json'
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
const catalogRelease = await findCatalogRelease();
|
||||
if (catalogRelease) {
|
||||
return catalogRelease;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
const row = response.result?.body
|
||||
?.split(/\r?\n/)
|
||||
.find(line => line.includes(`](${url})`));
|
||||
const checksum = row?.match(/`([a-fA-F0-9]{64})`/)?.[1];
|
||||
if (checksum) {
|
||||
return {
|
||||
version: this.version,
|
||||
url,
|
||||
checksum: { algorithm: 'sha256', value: checksum, source }
|
||||
};
|
||||
}
|
||||
// The current catalog also covers releases missing from GitHub, such as Corretto 15 and 16.
|
||||
const catalogRelease = await findCatalogRelease();
|
||||
if (catalogRelease) {
|
||||
return catalogRelease;
|
||||
}
|
||||
if (response.statusCode === _actions_http_client__WEBPACK_IMPORTED_MODULE_3__/* .HttpCodes */ .Hv.NotFound) {
|
||||
throw new Error(`Corretto release ${version} was not found in Amazon's GitHub releases or current download catalog`);
|
||||
}
|
||||
if (!row) {
|
||||
throw new Error(`No ${platform}/${arch} ${this.packageType} archive is listed for Corretto ${version}`);
|
||||
}
|
||||
throw new Error(`No SHA-256 checksum is published for Corretto ${version} (${platform}, ${arch}, ${this.packageType})`);
|
||||
}
|
||||
async getAvailableVersions() {
|
||||
const platform = this.getPlatformOption();
|
||||
const arch = this.distributionArchitecture();
|
||||
@@ -112,7 +197,7 @@ class CorrettoDistribution extends _base_installer_js__WEBPACK_IMPORTED_MODULE_4
|
||||
for (const version in eligibleVersions) {
|
||||
const availableVersion = eligibleVersions[version];
|
||||
for (const fileType in availableVersion) {
|
||||
const skipNonExtractableBinaries = fileType != (0,_util_js__WEBPACK_IMPORTED_MODULE_3__/* .getDownloadArchiveExtension */ .ag)();
|
||||
const skipNonExtractableBinaries = fileType != (0,_util_js__WEBPACK_IMPORTED_MODULE_4__/* .getDownloadArchiveExtension */ .ag)();
|
||||
if (skipNonExtractableBinaries) {
|
||||
continue;
|
||||
}
|
||||
@@ -139,7 +224,7 @@ class CorrettoDistribution extends _base_installer_js__WEBPACK_IMPORTED_MODULE_4
|
||||
case 'win32':
|
||||
return 'windows';
|
||||
case 'linux':
|
||||
return (0,_platform_types_js__WEBPACK_IMPORTED_MODULE_5__/* .isAlpineLinux */ .G6)() ? 'alpine' : 'linux';
|
||||
return (0,_platform_types_js__WEBPACK_IMPORTED_MODULE_6__/* .isAlpineLinux */ .G6)() ? 'alpine' : 'linux';
|
||||
default:
|
||||
return process.platform;
|
||||
}
|
||||
|
||||
Vendored
+3
-3
@@ -56,7 +56,7 @@ class LocalDistribution extends _base_installer_js__WEBPACK_IMPORTED_MODULE_3__/
|
||||
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)),
|
||||
Promise.all(/* import() */[__webpack_require__.e(824), __webpack_require__.e(219), __webpack_require__.e(779)]).then(__webpack_require__.bind(__webpack_require__, 5779)),
|
||||
hashFile(jdkFilePath)
|
||||
]);
|
||||
jdkCache = {
|
||||
@@ -70,7 +70,7 @@ class LocalDistribution extends _base_installer_js__WEBPACK_IMPORTED_MODULE_3__/
|
||||
};
|
||||
}
|
||||
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 { restoreJdk } = await Promise.all(/* import() */[__webpack_require__.e(824), __webpack_require__.e(219), __webpack_require__.e(779)]).then(__webpack_require__.bind(__webpack_require__, 5779));
|
||||
const restored = await restoreJdk(jdkCache);
|
||||
const restoredPath = restored
|
||||
? this.getRestoredJdkPath(this.version)
|
||||
@@ -94,7 +94,7 @@ class LocalDistribution extends _base_installer_js__WEBPACK_IMPORTED_MODULE_3__/
|
||||
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));
|
||||
const { registerJdk } = await Promise.all(/* import() */[__webpack_require__.e(824), __webpack_require__.e(219), __webpack_require__.e(779)]).then(__webpack_require__.bind(__webpack_require__, 5779));
|
||||
registerJdk(jdkCache);
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+56165
File diff suppressed because it is too large
Load Diff
Vendored
-326
@@ -1,326 +0,0 @@
|
||||
export const id = 220;
|
||||
export const ids = [220];
|
||||
export const modules = {
|
||||
|
||||
/***/ 3220:
|
||||
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
|
||||
// EXPORTS
|
||||
__webpack_require__.d(__webpack_exports__, {
|
||||
MicrosoftDistributions: () => (/* binding */ MicrosoftDistributions)
|
||||
});
|
||||
|
||||
// UNUSED EXPORTS: MICROSOFT_PUBLIC_KEY
|
||||
|
||||
// EXTERNAL MODULE: ./src/distributions/base-installer.ts + 2 modules
|
||||
var base_installer = __webpack_require__(6242);
|
||||
// EXTERNAL MODULE: ./src/util.ts
|
||||
var util = __webpack_require__(4527);
|
||||
// EXTERNAL MODULE: ./src/gpg.ts
|
||||
var gpg = __webpack_require__(8343);
|
||||
;// CONCATENATED MODULE: ./src/distributions/microsoft/microsoft-key.ts
|
||||
// Microsoft Build of OpenJDK GPG signing key
|
||||
// Retrieved from: https://download.visualstudio.microsoft.com/download/pr/b90071e2-e0cf-4411-98be-dbeb09d67bf0/8622862bcd54206e158c5abca0582c9b/464279_464280_aoc_20210208.asc
|
||||
const MICROSOFT_PUBLIC_KEY = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: BSN Pgp v1.1.0.0
|
||||
|
||||
mQENBGAhlWcBCADCQjj6huLTenvZSLej35e9YKEHm4lix2uvPOONexMaU8V2v7KL
|
||||
RGdoXF7jwHci7efnPZ+9zpS2+g3rhvv8M7yWy9E/1psEtGzvmp1IL/qIabMEQqi+
|
||||
UlhPGh7MQ/BkXAlic8Dyl3XYqr0EXS11iCiTr6Zkxs9Ee4V54gxL4gogRn4wk9sl
|
||||
/nrjgDzMsUwla0pynoQQvYpqCdiAr3gKKllT1skCDqgVOMMyZxsx9HjZxg/3AJz6
|
||||
r5i512L2R+3Hkv+XmxT+mnGBCFcny0DM7PjNXEmIK3ZSkro1tQML90zx3Fyh5esx
|
||||
fpVvuIXGFV75o35VVCBZoiD3hcfOnIJsPQ9nABEBAAG0OE1pY3Jvc29mdCBKYXZh
|
||||
IEVuZ2luZWVyaW5nIDxqYXZhcGxhdGluZnJhQG1pY3Jvc29mdC5jb20+iQE4BBMB
|
||||
CAAiBQJgIZVnAhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRA1Ux0xWyHB
|
||||
icwTCACJO2FGNocNvdUtAb+eDKuGwt0chAJdCES2ZtgBScwrwDyWpxpRznoXWBHL
|
||||
MJeLyxJoKsCG3vVlY4uh48psCzVm3OKvi7MCPT955t8W6TzfSBxTpjR8zRgJkjPJ
|
||||
EGhHTlusUfz7TtM5etJF0qscSJH1grcNsgtee97mk4QyEzT8Di83NQmYxKcBrliq
|
||||
yK/SWWt8VkTyYAEO6L5PoB4L9r8ka27uQs+jgCw+/Z0JMtNmmhyNGY3+a1YtPeoy
|
||||
JdQaI9LphfKGbVaz6SK2aol7vj+c2TG3TLUYdOYGMH1OZlri2GTkCVjwna2GC7p4
|
||||
Fa133tP85xzJEq1XeXm8WeLFo2wV
|
||||
=rHCS
|
||||
-----END PGP PUBLIC KEY BLOCK-----`;
|
||||
|
||||
// EXTERNAL MODULE: ./src/constants.ts
|
||||
var constants = __webpack_require__(7242);
|
||||
// EXTERNAL MODULE: ./node_modules/@actions/core/lib/core.js + 7 modules
|
||||
var core = __webpack_require__(3838);
|
||||
// EXTERNAL MODULE: ./node_modules/@actions/tool-cache/lib/tool-cache.js + 2 modules
|
||||
var tool_cache = __webpack_require__(9805);
|
||||
// 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_);
|
||||
;// CONCATENATED MODULE: ./src/distributions/microsoft/installer.ts
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class MicrosoftDistributions extends base_installer/* JavaBase */.O {
|
||||
constructor(installerOptions) {
|
||||
super('Microsoft', installerOptions);
|
||||
}
|
||||
async downloadTool(javaRelease) {
|
||||
core/* info */.pq(`Downloading Java ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...`);
|
||||
let javaArchivePath = await this.downloadAndVerify(javaRelease);
|
||||
if (this.verifySignature) {
|
||||
try {
|
||||
if (!javaRelease.signatureUrl) {
|
||||
throw new Error(`Input 'verify-signature' is enabled, but no signature URL was found for Microsoft Build of OpenJDK version ${javaRelease.version}.`);
|
||||
}
|
||||
core/* info */.pq(`Verifying Java package signature...`);
|
||||
try {
|
||||
await gpg/* verifyPackageSignature */.Yi(javaArchivePath, javaRelease.signatureUrl, this.verifySignaturePublicKey ?? MICROSOFT_PUBLIC_KEY);
|
||||
}
|
||||
catch (error) {
|
||||
throw new Error(`Failed to verify signature for Microsoft Build of OpenJDK version ${javaRelease.version}. Signature URL: ${javaRelease.signatureUrl}. Error: ${error.message} ${constants/* SIGNATURE_VERIFICATION_FAILURE_HELP */.kQ}`, { cause: error });
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
if (this.verifySignatureExplicitlyRequested) {
|
||||
throw error;
|
||||
}
|
||||
core/* warning */.$e(error instanceof Error ? error.message : `Unknown error: ${error}`);
|
||||
}
|
||||
}
|
||||
core/* info */.pq(`Extracting Java archive...`);
|
||||
const extension = (0,util/* getDownloadArchiveExtension */.ag)();
|
||||
if (process.platform === 'win32') {
|
||||
javaArchivePath = (0,util/* renameWinArchive */.n2)(javaArchivePath);
|
||||
}
|
||||
const extractedJavaPath = await (0,util/* extractJdkFile */.PE)(javaArchivePath, extension);
|
||||
const archiveName = external_fs_default().readdirSync(extractedJavaPath)[0];
|
||||
const archivePath = external_path_default().join(extractedJavaPath, archiveName);
|
||||
const javaPath = await (0,util/* cacheJdkDir */.Vj)(archivePath, this.toolcacheFolderName, this.getToolcacheVersionName(javaRelease.version), this.architecture);
|
||||
return { version: javaRelease.version, path: javaPath };
|
||||
}
|
||||
async findPackageForDownload(range) {
|
||||
const arch = this.distributionArchitecture();
|
||||
if (arch !== 'x64' && arch !== 'aarch64') {
|
||||
throw new Error(`Unsupported architecture: ${this.architecture}`);
|
||||
}
|
||||
if (!this.stable) {
|
||||
throw new Error('Early access versions are not supported');
|
||||
}
|
||||
if (this.packageType !== 'jdk') {
|
||||
throw new Error('Microsoft Build of OpenJDK provides only the `jdk` package type');
|
||||
}
|
||||
const manifest = await this.getAvailableVersions();
|
||||
if (!manifest) {
|
||||
throw new Error('Could not load manifest for Microsoft Build of OpenJDK');
|
||||
}
|
||||
const foundRelease = await tool_cache/* findFromManifest */.DC(range, true, manifest, arch);
|
||||
if (!foundRelease) {
|
||||
const availableVersionStrings = manifest.map(item => item.version);
|
||||
throw this.createVersionNotFoundError(range, availableVersionStrings);
|
||||
}
|
||||
const file = foundRelease.files[0];
|
||||
const signatureUrl = file.signature_url ?? `${file.download_url}.sig`;
|
||||
return {
|
||||
url: file.download_url,
|
||||
signatureUrl,
|
||||
version: foundRelease.version,
|
||||
checksum: await this.fetchChecksum(`${file.download_url}.sha256sum.txt`, 'sha256')
|
||||
};
|
||||
}
|
||||
supportsSignatureVerification() {
|
||||
return true;
|
||||
}
|
||||
async getAvailableVersions() {
|
||||
let releases = null;
|
||||
const fileUrl = `https://aka.ms/download-jdk/microsoft-openjdk-versions.json`;
|
||||
// Avoid leaking the GitHub token to a non-GitHub host (aka.ms redirects to a CDN).
|
||||
const headers = { accept: 'application/json' };
|
||||
let response = null;
|
||||
if (core/* isDebug */._o()) {
|
||||
console.time('Retrieving available versions for Microsoft took'); // eslint-disable-line no-console
|
||||
}
|
||||
try {
|
||||
response = await this.http.getJson(fileUrl, headers);
|
||||
if (!response.result) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
core/* debug */.Yz(`Http request for microsoft-openjdk-versions.json failed with status code: ${response?.statusCode}. Error: ${err}`);
|
||||
return null;
|
||||
}
|
||||
if (response.result) {
|
||||
releases = response.result;
|
||||
}
|
||||
if (core/* isDebug */._o() && releases) {
|
||||
core/* startGroup */.Oh('Print information about available versions');
|
||||
console.timeEnd('Retrieving available versions for Microsoft took'); // eslint-disable-line no-console
|
||||
core/* debug */.Yz(`Available versions: [${releases.length}]`);
|
||||
core/* debug */.Yz(releases.map(item => item.version).join(', '));
|
||||
core/* endGroup */.N4();
|
||||
}
|
||||
return releases;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 8343:
|
||||
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ Fh: () => (/* binding */ importKey),
|
||||
/* harmony export */ Yi: () => (/* binding */ verifyPackageSignature),
|
||||
/* harmony export */ mS: () => (/* binding */ removeGpgHome),
|
||||
/* harmony export */ nY: () => (/* binding */ toGpgPath),
|
||||
/* harmony export */ o6: () => (/* binding */ isGpgAvailable)
|
||||
/* harmony export */ });
|
||||
/* unused harmony export GPG_HOME_PREFIX */
|
||||
/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(9896);
|
||||
/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_0__);
|
||||
/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6928);
|
||||
/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__);
|
||||
/* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6982);
|
||||
/* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(crypto__WEBPACK_IMPORTED_MODULE_2__);
|
||||
/* harmony import */ var _actions_io__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8701);
|
||||
/* harmony import */ var _actions_exec__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(5260);
|
||||
/* harmony import */ var _actions_tool_cache__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(9805);
|
||||
/* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(4527);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const GPG_HOME_PREFIX = 'setup-java-gpg-';
|
||||
const VERIFY_GPG_HOME_PREFIX = 'verify-signature-gpg-home-';
|
||||
async function isGpgAvailable() {
|
||||
return Boolean(await _actions_io__WEBPACK_IMPORTED_MODULE_3__/* .which */ .K7('gpg', false));
|
||||
}
|
||||
// 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
|
||||
// internally. Passing Windows paths with backslashes can cause fatal GPG errors
|
||||
// (exit code 2), so all paths passed to GPG must be in POSIX format on Windows.
|
||||
function toGpgPath(p) {
|
||||
if (process.platform !== 'win32')
|
||||
return p;
|
||||
return p
|
||||
.replace(/\\/g, '/')
|
||||
.replace(/^([A-Za-z]):\//, (_, drive) => `/${drive.toLowerCase()}/`);
|
||||
}
|
||||
function createGpgHome(prefix, tempDir = _util_js__WEBPACK_IMPORTED_MODULE_6__/* .getTempDir */ .G4()) {
|
||||
const gpgHome = fs__WEBPACK_IMPORTED_MODULE_0__.mkdtempSync(path__WEBPACK_IMPORTED_MODULE_1__.join(tempDir, prefix));
|
||||
if (process.platform !== 'win32') {
|
||||
fs__WEBPACK_IMPORTED_MODULE_0__.chmodSync(gpgHome, 0o700);
|
||||
}
|
||||
return gpgHome;
|
||||
}
|
||||
async function importKey(privateKey) {
|
||||
const gpgHome = createGpgHome(GPG_HOME_PREFIX);
|
||||
const privateKeyFile = path__WEBPACK_IMPORTED_MODULE_1__.join(gpgHome, `private-key-${(0,crypto__WEBPACK_IMPORTED_MODULE_2__.randomUUID)()}.asc`);
|
||||
try {
|
||||
fs__WEBPACK_IMPORTED_MODULE_0__.writeFileSync(privateKeyFile, privateKey, {
|
||||
encoding: 'utf-8',
|
||||
flag: 'wx',
|
||||
mode: 0o600
|
||||
});
|
||||
try {
|
||||
await _actions_exec__WEBPACK_IMPORTED_MODULE_4__/* .exec */ .m('gpg', [
|
||||
'--homedir',
|
||||
toGpgPath(gpgHome),
|
||||
'--batch',
|
||||
'--import',
|
||||
toGpgPath(privateKeyFile)
|
||||
], { silent: true });
|
||||
}
|
||||
finally {
|
||||
fs__WEBPACK_IMPORTED_MODULE_0__.rmSync(privateKeyFile, { force: true });
|
||||
}
|
||||
return gpgHome;
|
||||
}
|
||||
catch (error) {
|
||||
await removeGpgHome(gpgHome);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async function removeGpgHome(gpgHome) {
|
||||
if (!gpgHome) {
|
||||
return;
|
||||
}
|
||||
const resolvedGpgHome = path__WEBPACK_IMPORTED_MODULE_1__.resolve(gpgHome);
|
||||
const resolvedTempDir = path__WEBPACK_IMPORTED_MODULE_1__.resolve(_util_js__WEBPACK_IMPORTED_MODULE_6__/* .getTempDir */ .G4());
|
||||
if (path__WEBPACK_IMPORTED_MODULE_1__.dirname(resolvedGpgHome) !== resolvedTempDir ||
|
||||
!path__WEBPACK_IMPORTED_MODULE_1__.basename(resolvedGpgHome).startsWith(GPG_HOME_PREFIX)) {
|
||||
throw new Error(`Refusing to remove unexpected GPG home: ${gpgHome}`);
|
||||
}
|
||||
if (!fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(resolvedGpgHome)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await _actions_exec__WEBPACK_IMPORTED_MODULE_4__/* .exec */ .m('gpgconf', ['--homedir', toGpgPath(resolvedGpgHome), '--kill', 'gpg-agent'], { silent: true, ignoreReturnCode: true });
|
||||
}
|
||||
catch {
|
||||
// gpgconf may be unavailable, but directory removal must still be attempted.
|
||||
}
|
||||
await _actions_io__WEBPACK_IMPORTED_MODULE_3__/* .rmRF */ .Yz(resolvedGpgHome);
|
||||
}
|
||||
async function verifyPackageSignature(archivePath, signatureUrl, publicKeyContent) {
|
||||
const signaturePath = await _actions_tool_cache__WEBPACK_IMPORTED_MODULE_5__/* .downloadTool */ .bq(signatureUrl);
|
||||
let gpgHome;
|
||||
try {
|
||||
// Both RUNNER_TEMP and TMPDIR can exceed macOS's 104-byte agent socket limit.
|
||||
const tempDir = process.platform === 'darwin' ? '/tmp' : _util_js__WEBPACK_IMPORTED_MODULE_6__/* .getTempDir */ .G4();
|
||||
gpgHome = createGpgHome(VERIFY_GPG_HOME_PREFIX, tempDir);
|
||||
}
|
||||
catch (error) {
|
||||
try {
|
||||
await _actions_io__WEBPACK_IMPORTED_MODULE_3__/* .rmRF */ .Yz(signaturePath);
|
||||
}
|
||||
catch {
|
||||
// ignore cleanup failures
|
||||
}
|
||||
throw new Error(`Failed to create temporary GPG home directory for signature verification: ${error.message}`, { cause: error });
|
||||
}
|
||||
try {
|
||||
const publicKeys = Array.isArray(publicKeyContent)
|
||||
? publicKeyContent
|
||||
: [publicKeyContent];
|
||||
const publicKeyFiles = publicKeys.map((publicKey, index) => {
|
||||
const publicKeyFile = path__WEBPACK_IMPORTED_MODULE_1__.join(gpgHome, `public-key-${index}.asc`);
|
||||
fs__WEBPACK_IMPORTED_MODULE_0__.writeFileSync(publicKeyFile, publicKey, { encoding: 'utf-8' });
|
||||
return toGpgPath(publicKeyFile);
|
||||
});
|
||||
const options = { silent: true };
|
||||
await _actions_exec__WEBPACK_IMPORTED_MODULE_4__/* .exec */ .m('gpg', [
|
||||
'--homedir',
|
||||
toGpgPath(gpgHome),
|
||||
'--batch',
|
||||
'--import',
|
||||
...publicKeyFiles
|
||||
], options);
|
||||
await _actions_exec__WEBPACK_IMPORTED_MODULE_4__/* .exec */ .m('gpg', [
|
||||
'--homedir',
|
||||
toGpgPath(gpgHome),
|
||||
'--batch',
|
||||
'--verify',
|
||||
toGpgPath(signaturePath),
|
||||
toGpgPath(archivePath)
|
||||
], options);
|
||||
}
|
||||
finally {
|
||||
await _actions_io__WEBPACK_IMPORTED_MODULE_3__/* .rmRF */ .Yz(signaturePath);
|
||||
await _actions_io__WEBPACK_IMPORTED_MODULE_3__/* .rmRF */ .Yz(gpgHome);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***/ })
|
||||
|
||||
};
|
||||
Vendored
+19
-16
@@ -360,7 +360,7 @@ class JavaBase {
|
||||
? await this.createJdkCache(javaRelease)
|
||||
: undefined;
|
||||
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 { restoreJdk } = await Promise.all(/* import() */[__webpack_require__.e(824), __webpack_require__.e(219), __webpack_require__.e(779)]).then(__webpack_require__.bind(__webpack_require__, 5779));
|
||||
const restored = await restoreJdk(jdkCache);
|
||||
if (restored) {
|
||||
const restoredPath = this.getRestoredJdkPath(javaRelease.version);
|
||||
@@ -378,7 +378,7 @@ class JavaBase {
|
||||
core/* info */.pq(`Java ${foundJava.version} was downloaded`);
|
||||
if (javaRelease.floating) {
|
||||
if (!semver_default().valid(foundJava.version) ||
|
||||
!(0,util/* isVersionSatisfies */.y)(this.version, foundJava.version)) {
|
||||
!this.isVersionSatisfied(this.version, foundJava.version)) {
|
||||
throw new Error(`The downloaded ${this.distribution} artifact reported Java ${foundJava.version}, which does not satisfy '${this.version}'.`);
|
||||
}
|
||||
javaRelease = { ...javaRelease, version: foundJava.version };
|
||||
@@ -392,7 +392,7 @@ class JavaBase {
|
||||
// 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));
|
||||
const { registerJdk } = await Promise.all(/* import() */[__webpack_require__.e(824), __webpack_require__.e(219), __webpack_require__.e(779)]).then(__webpack_require__.bind(__webpack_require__, 5779));
|
||||
registerJdk(jdkCache);
|
||||
}
|
||||
}
|
||||
@@ -441,7 +441,7 @@ class JavaBase {
|
||||
const release = await this.findPackageForDownload(this.version);
|
||||
return this.restoreFloatingResolution(release);
|
||||
}
|
||||
const { restoreJdkResolution, registerJdkResolution } = await Promise.all(/* import() */[__webpack_require__.e(824), __webpack_require__.e(971), __webpack_require__.e(348)]).then(__webpack_require__.bind(__webpack_require__, 967));
|
||||
const { restoreJdkResolution, registerJdkResolution } = await Promise.all(/* import() */[__webpack_require__.e(824), __webpack_require__.e(219), __webpack_require__.e(348)]).then(__webpack_require__.bind(__webpack_require__, 967));
|
||||
const request = {
|
||||
distribution: this.distribution,
|
||||
packageType: this.packageType,
|
||||
@@ -477,7 +477,7 @@ class JavaBase {
|
||||
return false;
|
||||
}
|
||||
async createJdkCache(javaRelease) {
|
||||
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));
|
||||
const { getJdkVerificationIdentity } = await Promise.all(/* import() */[__webpack_require__.e(824), __webpack_require__.e(219), __webpack_require__.e(779)]).then(__webpack_require__.bind(__webpack_require__, 5779));
|
||||
return {
|
||||
distribution: this.distribution,
|
||||
packageType: this.packageType,
|
||||
@@ -495,13 +495,13 @@ class JavaBase {
|
||||
this.forceDownload) {
|
||||
return javaRelease;
|
||||
}
|
||||
const { restoreJdkResolution } = await Promise.all(/* import() */[__webpack_require__.e(824), __webpack_require__.e(971), __webpack_require__.e(348)]).then(__webpack_require__.bind(__webpack_require__, 967));
|
||||
const { restoreJdkResolution } = await Promise.all(/* import() */[__webpack_require__.e(824), __webpack_require__.e(219), __webpack_require__.e(348)]).then(__webpack_require__.bind(__webpack_require__, 967));
|
||||
const restored = await restoreJdkResolution(this.getFloatingResolutionRequest(javaRelease));
|
||||
if (!restored) {
|
||||
return javaRelease;
|
||||
}
|
||||
if (!semver_default().valid(restored.release.version) ||
|
||||
!(0,util/* isVersionSatisfies */.y)(this.version, restored.release.version)) {
|
||||
!this.isVersionSatisfied(this.version, restored.release.version)) {
|
||||
core/* debug */.Yz(`Ignoring the cached concrete version '${restored.release.version}' for ${this.distribution} ${this.version}.`);
|
||||
return javaRelease;
|
||||
}
|
||||
@@ -515,7 +515,7 @@ class JavaBase {
|
||||
this.forceDownload) {
|
||||
return;
|
||||
}
|
||||
const { registerJdkResolution } = await Promise.all(/* import() */[__webpack_require__.e(824), __webpack_require__.e(971), __webpack_require__.e(348)]).then(__webpack_require__.bind(__webpack_require__, 967));
|
||||
const { registerJdkResolution } = await Promise.all(/* import() */[__webpack_require__.e(824), __webpack_require__.e(219), __webpack_require__.e(348)]).then(__webpack_require__.bind(__webpack_require__, 967));
|
||||
registerJdkResolution(this.getFloatingResolutionRequest(javaRelease), javaRelease);
|
||||
}
|
||||
getFloatingResolutionRequest(javaRelease) {
|
||||
@@ -684,10 +684,10 @@ class JavaBase {
|
||||
})
|
||||
.filter(item => item.stable === this.stable);
|
||||
const satisfiedVersions = availableVersions
|
||||
.filter(item => (0,util/* isVersionSatisfies */.y)(this.version, item.version))
|
||||
.filter(item => this.isVersionSatisfied(this.version, item.version))
|
||||
.filter(item => item.path)
|
||||
.sort((a, b) => {
|
||||
return -semver_default().compareBuild(a.version, b.version);
|
||||
return -this.compareVersions(a.version, b.version);
|
||||
});
|
||||
if (!satisfiedVersions || satisfiedVersions.length === 0) {
|
||||
return null;
|
||||
@@ -697,6 +697,12 @@ class JavaBase {
|
||||
path: satisfiedVersions[0].path
|
||||
};
|
||||
}
|
||||
isVersionSatisfied(range, version) {
|
||||
return (0,util/* isVersionSatisfies */.y)(range, version);
|
||||
}
|
||||
compareVersions(a, b) {
|
||||
return semver_default().compareBuild(a, b);
|
||||
}
|
||||
normalizeVersion(version) {
|
||||
let stable = true;
|
||||
const latest = false;
|
||||
@@ -730,12 +736,9 @@ class JavaBase {
|
||||
}
|
||||
// Java uses a versioning scheme (JEP 322) that can contain more numeric
|
||||
// fields than SemVer allows, e.g. '18.0.1.1' or '11.0.9.1'. Convert such
|
||||
// exact versions to SemVer build notation ('18.0.1+1') so they are
|
||||
// accepted. Ranges and versions that already carry build metadata are
|
||||
// left untouched.
|
||||
if (/^\d+(\.\d+){3,}$/.test(version)) {
|
||||
version = (0,util/* convertVersionToSemver */.ZY)(version);
|
||||
}
|
||||
// exact versions to SemVer build notation ('18.0.1+1', or '26.0.2+1.1'
|
||||
// for '26.0.2.1+1') so they are accepted. Ranges are left untouched.
|
||||
version = (0,util/* normalizeJavaVersionToSemver */.zZ)(version);
|
||||
if (!semver_default().validRange(version)) {
|
||||
throw new Error(`The string '${version}' is not valid SemVer notation for a Java version. Please check README file for code snippets and more detailed information`);
|
||||
}
|
||||
|
||||
Vendored
+41
-3
@@ -34,16 +34,16 @@ class JetBrainsDistribution extends _base_installer_js__WEBPACK_IMPORTED_MODULE_
|
||||
async findPackageForDownload(range) {
|
||||
const versionsRaw = await this.getAvailableVersions();
|
||||
const versions = versionsRaw.map(v => {
|
||||
const formattedVersion = `${v.semver}+${v.build}`;
|
||||
const formattedVersion = (0,_util_js__WEBPACK_IMPORTED_MODULE_5__/* .normalizeJavaVersionToSemver */ .zZ)(`${v.semver}+${v.build}`);
|
||||
return {
|
||||
version: formattedVersion,
|
||||
url: v.url
|
||||
};
|
||||
});
|
||||
const satisfiedVersions = versions
|
||||
.filter(item => (0,_util_js__WEBPACK_IMPORTED_MODULE_5__/* .isVersionSatisfies */ .y)(range, item.version))
|
||||
.filter(item => this.isVersionSatisfied(range, item.version))
|
||||
.sort((a, b) => {
|
||||
return -semver__WEBPACK_IMPORTED_MODULE_3___default().compareBuild(a.version, b.version);
|
||||
return -this.compareVersions(a.version, b.version);
|
||||
});
|
||||
const resolvedFullVersion = satisfiedVersions.length > 0 ? satisfiedVersions[0] : null;
|
||||
if (!resolvedFullVersion) {
|
||||
@@ -59,6 +59,44 @@ class JetBrainsDistribution extends _base_installer_js__WEBPACK_IMPORTED_MODULE_
|
||||
checksum: await this.fetchChecksum(`${resolvedFullVersion.url}.checksum`, ['sha512', 'sha256'])
|
||||
};
|
||||
}
|
||||
normalizeVersion(version) {
|
||||
const normalized = super.normalizeVersion(version);
|
||||
if (/^\d+\.\d+\.\d+(?:\.\d+)+(?:-ea)?$/.test(version)) {
|
||||
// A patch-only request must match any JBR build of that patch.
|
||||
normalized.version = version.replace(/-ea$/, '');
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
isVersionSatisfied(range, version) {
|
||||
const patchRequest = range.match(/^(\d+\.\d+\.\d+)((?:\.\d+)+)$/);
|
||||
if (patchRequest) {
|
||||
const candidate = semver__WEBPACK_IMPORTED_MODULE_3___default().parse(version);
|
||||
return (candidate !== null &&
|
||||
semver__WEBPACK_IMPORTED_MODULE_3___default().eq(patchRequest[1], candidate) &&
|
||||
(candidate.build.slice(0, -2).join('.') || '0') ===
|
||||
patchRequest[2].slice(1));
|
||||
}
|
||||
return super.isVersionSatisfied(range, version);
|
||||
}
|
||||
compareVersions(a, b) {
|
||||
const mainComparison = semver__WEBPACK_IMPORTED_MODULE_3___default().compare(a, b);
|
||||
if (mainComparison !== 0) {
|
||||
return mainComparison;
|
||||
}
|
||||
// JBR builds have two components (one in older releases). Any preceding
|
||||
// metadata components are Java patch fields and take priority over builds.
|
||||
const first = new (semver__WEBPACK_IMPORTED_MODULE_3___default().SemVer)(a).build;
|
||||
const second = new (semver__WEBPACK_IMPORTED_MODULE_3___default().SemVer)(b).build;
|
||||
const firstPatch = first.slice(0, -2);
|
||||
const secondPatch = second.slice(0, -2);
|
||||
for (let index = 0; index < Math.max(firstPatch.length, secondPatch.length); index++) {
|
||||
const patchComparison = semver__WEBPACK_IMPORTED_MODULE_3___default().compareIdentifiers(firstPatch[index] ?? '0', secondPatch[index] ?? '0');
|
||||
if (patchComparison !== 0) {
|
||||
return patchComparison;
|
||||
}
|
||||
}
|
||||
return semver__WEBPACK_IMPORTED_MODULE_3___default().compareBuild(first.length ? `0.0.0+${first.slice(-2).join('.')}` : '0.0.0', second.length ? `0.0.0+${second.slice(-2).join('.')}` : '0.0.0');
|
||||
}
|
||||
async downloadTool(javaRelease) {
|
||||
_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .info */ .pq(`Downloading Java ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...`);
|
||||
const javaArchivePath = await this.downloadAndVerify(javaRelease);
|
||||
|
||||
Vendored
+1
-1
@@ -16,7 +16,7 @@ export const modules = {
|
||||
/* 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_cache__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4219);
|
||||
/* harmony import */ var _actions_core__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(3838);
|
||||
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -14,7 +14,7 @@ export const modules = {
|
||||
/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__);
|
||||
/* harmony import */ var os__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(857);
|
||||
/* harmony import */ var os__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(os__WEBPACK_IMPORTED_MODULE_1__);
|
||||
/* harmony import */ var _actions_cache__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6971);
|
||||
/* harmony import */ var _actions_cache__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4219);
|
||||
/* harmony import */ var _actions_core__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(3838);
|
||||
/* harmony import */ var _actions_glob__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(2377);
|
||||
/**
|
||||
|
||||
Vendored
+1
-1
@@ -8,7 +8,7 @@ export const modules = {
|
||||
/* 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_cache__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4219);
|
||||
/* harmony import */ var _actions_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3838);
|
||||
/* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4527);
|
||||
|
||||
|
||||
Vendored
+37
-5
@@ -109,7 +109,7 @@ class TemurinDistribution extends base_installer/* JavaBase */.O {
|
||||
const formattedVersion = this.stable
|
||||
? item.version_data.semver
|
||||
: item.version_data.semver.replace('-beta+', '+');
|
||||
return {
|
||||
const release = {
|
||||
version: formattedVersion,
|
||||
url: item.binaries[0].package.link,
|
||||
signatureUrl: item.binaries[0].package.signature_link,
|
||||
@@ -119,15 +119,29 @@ class TemurinDistribution extends base_installer/* JavaBase */.O {
|
||||
source: item.binaries[0].package.checksum_link
|
||||
}
|
||||
};
|
||||
return {
|
||||
release,
|
||||
openjdkVersion: getOpenJdkSemverVersion(item.version_data)
|
||||
};
|
||||
});
|
||||
// The Adoptium API `semver` folds the JEP 322 patch field into the build
|
||||
// number ('26.0.2.1+1' -> '26.0.2+101') and appends extra metadata for LTS
|
||||
// releases ('25.0.4+7' -> '25.0.4+7.0.LTS'). Exact versions requested by
|
||||
// users follow the OpenJDK notation instead, so also match them against a
|
||||
// key derived from the OpenJDK version fields ('26.0.2+1.1', '25.0.4+7').
|
||||
const isExactBuildRequest = (semver_default().parse(version)?.build.length ?? 0) > 0;
|
||||
const satisfiedVersions = availableVersionsWithBinaries
|
||||
.filter(item => (0,util/* isVersionSatisfies */.y)(version, item.version))
|
||||
.filter(({ release, openjdkVersion }) => (0,util/* isVersionSatisfies */.y)(version, release.version) ||
|
||||
(isExactBuildRequest &&
|
||||
openjdkVersion !== null &&
|
||||
semver_default().compareBuild(version, openjdkVersion) === 0))
|
||||
.map(({ release }) => release)
|
||||
.sort((a, b) => {
|
||||
return -semver_default().compareBuild(a.version, b.version);
|
||||
});
|
||||
const resolvedFullVersion = satisfiedVersions.length > 0 ? satisfiedVersions[0] : null;
|
||||
if (!resolvedFullVersion) {
|
||||
const availableVersionStrings = availableVersionsWithBinaries.map(item => item.version);
|
||||
const availableVersionStrings = availableVersionsWithBinaries.map(({ release }) => release.version);
|
||||
throw this.createVersionNotFoundError(version, availableVersionStrings);
|
||||
}
|
||||
return resolvedFullVersion;
|
||||
@@ -279,6 +293,20 @@ class TemurinDistribution extends base_installer/* JavaBase */.O {
|
||||
return architecture === 'armv7' ? 'arm' : architecture;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Builds a SemVer version from the OpenJDK version fields reported by the
|
||||
* Adoptium API, e.g. '26.0.2.1+1' -> '26.0.2+1.1' and '25.0.4+7-LTS' ->
|
||||
* '25.0.4+7'. Returns null if the fields cannot form a valid SemVer version.
|
||||
*/
|
||||
function getOpenJdkSemverVersion(versionData) {
|
||||
const { major, minor, security, patch, build } = versionData;
|
||||
if (build === undefined || build === null) {
|
||||
return null;
|
||||
}
|
||||
const buildMetadata = patch ? `${patch}.${build}` : `${build}`;
|
||||
const version = `${major}.${minor}.${security}+${buildMetadata}`;
|
||||
return semver_default().valid(version) ? version : null;
|
||||
}
|
||||
|
||||
|
||||
/***/ }),
|
||||
@@ -375,13 +403,16 @@ async function removeGpgHome(gpgHome) {
|
||||
if (!fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(resolvedGpgHome)) {
|
||||
return;
|
||||
}
|
||||
await stopGpgAgent(resolvedGpgHome);
|
||||
await _actions_io__WEBPACK_IMPORTED_MODULE_3__/* .rmRF */ .Yz(resolvedGpgHome);
|
||||
}
|
||||
async function stopGpgAgent(gpgHome) {
|
||||
try {
|
||||
await _actions_exec__WEBPACK_IMPORTED_MODULE_4__/* .exec */ .m('gpgconf', ['--homedir', toGpgPath(resolvedGpgHome), '--kill', 'gpg-agent'], { silent: true, ignoreReturnCode: true });
|
||||
await _actions_exec__WEBPACK_IMPORTED_MODULE_4__/* .exec */ .m('gpgconf', ['--homedir', toGpgPath(gpgHome), '--kill', 'gpg-agent'], { silent: true, ignoreReturnCode: true });
|
||||
}
|
||||
catch {
|
||||
// gpgconf may be unavailable, but directory removal must still be attempted.
|
||||
}
|
||||
await _actions_io__WEBPACK_IMPORTED_MODULE_3__/* .rmRF */ .Yz(resolvedGpgHome);
|
||||
}
|
||||
async function verifyPackageSignature(archivePath, signatureUrl, publicKeyContent) {
|
||||
const signaturePath = await _actions_tool_cache__WEBPACK_IMPORTED_MODULE_5__/* .downloadTool */ .bq(signatureUrl);
|
||||
@@ -427,6 +458,7 @@ async function verifyPackageSignature(archivePath, signatureUrl, publicKeyConten
|
||||
], options);
|
||||
}
|
||||
finally {
|
||||
await stopGpgAgent(gpgHome);
|
||||
await _actions_io__WEBPACK_IMPORTED_MODULE_3__/* .rmRF */ .Yz(signaturePath);
|
||||
await _actions_io__WEBPACK_IMPORTED_MODULE_3__/* .rmRF */ .Yz(gpgHome);
|
||||
}
|
||||
|
||||
Vendored
+56
-3
@@ -52,14 +52,58 @@ class LibericaDistributions extends _base_installer_js__WEBPACK_IMPORTED_MODULE_
|
||||
version: this.convertVersionToSemver(item)
|
||||
}));
|
||||
const satisfiedVersion = availableVersions
|
||||
.filter(item => (0,_util_js__WEBPACK_IMPORTED_MODULE_2__/* .isVersionSatisfies */ .y)(range, item.version))
|
||||
.sort((a, b) => -semver__WEBPACK_IMPORTED_MODULE_1___default().compareBuild(a.version, b.version))[0];
|
||||
.filter(item => this.isVersionSatisfied(range, item.version))
|
||||
.sort((a, b) => -this.compareVersions(a.version, b.version))[0];
|
||||
if (!satisfiedVersion) {
|
||||
const availableVersionStrings = availableVersions.map(item => item.version);
|
||||
throw this.createVersionNotFoundError(range, availableVersionStrings);
|
||||
}
|
||||
return satisfiedVersion;
|
||||
}
|
||||
normalizeVersion(version) {
|
||||
const normalized = super.normalizeVersion(version);
|
||||
if (/^\d+\.\d+\.\d+(?:\.\d+)+(?:-ea)?$/.test(version)) {
|
||||
// Keep patch-only requests distinct from exact SDKMAN build requests.
|
||||
normalized.version = version.replace(/-ea$/, '');
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
isVersionSatisfied(range, version) {
|
||||
const patchRequest = range.match(/^(\d+\.\d+\.\d+)((?:\.\d+)+)$/);
|
||||
if (patchRequest) {
|
||||
const candidate = semver__WEBPACK_IMPORTED_MODULE_1___default().parse(version);
|
||||
if (!candidate || !semver__WEBPACK_IMPORTED_MODULE_1___default().eq(patchRequest[1], candidate)) {
|
||||
return false;
|
||||
}
|
||||
const patch = candidate.build.length > 1
|
||||
? candidate.build.slice(0, -1).join('.')
|
||||
: '0';
|
||||
return patch === patchRequest[2].slice(1);
|
||||
}
|
||||
return super.isVersionSatisfied(range, version);
|
||||
}
|
||||
compareVersions(a, b) {
|
||||
const mainComparison = semver__WEBPACK_IMPORTED_MODULE_1___default().compare(a, b);
|
||||
if (mainComparison !== 0) {
|
||||
return mainComparison;
|
||||
}
|
||||
const getPatchAndBuild = (version) => {
|
||||
const build = new (semver__WEBPACK_IMPORTED_MODULE_1___default().SemVer)(version).build;
|
||||
return build.length > 1
|
||||
? { patch: build.slice(0, -1), build: build[build.length - 1] }
|
||||
: { patch: [], build: build[0] ?? '0' };
|
||||
};
|
||||
const first = getPatchAndBuild(a);
|
||||
const second = getPatchAndBuild(b);
|
||||
const compareNumericIdentifiers = (left, right) => semver__WEBPACK_IMPORTED_MODULE_1___default().compareBuild(`0.0.0+${left}`, `0.0.0+${right}`);
|
||||
for (let index = 0; index < Math.max(first.patch.length, second.patch.length); index++) {
|
||||
const patchComparison = compareNumericIdentifiers(first.patch[index] ?? '0', second.patch[index] ?? '0');
|
||||
if (patchComparison !== 0) {
|
||||
return patchComparison;
|
||||
}
|
||||
}
|
||||
return compareNumericIdentifiers(first.build, second.build);
|
||||
}
|
||||
async getAvailableVersions() {
|
||||
if (_actions_core__WEBPACK_IMPORTED_MODULE_3__/* .isDebug */ ._o()) {
|
||||
console.time('Retrieving available versions for Liberica took'); // eslint-disable-line no-console
|
||||
@@ -128,8 +172,17 @@ class LibericaDistributions extends _base_installer_js__WEBPACK_IMPORTED_MODULE_
|
||||
}
|
||||
}
|
||||
convertVersionToSemver(version) {
|
||||
const { buildVersion, featureVersion, interimVersion, updateVersion } = version;
|
||||
const { featureVersion, interimVersion, updateVersion } = version;
|
||||
const buildVersion = version.version.split('+')[1] || version.buildVersion;
|
||||
const mainVersion = [featureVersion, interimVersion, updateVersion].join('.');
|
||||
const patchVersion = version.version
|
||||
.split('+')[0]
|
||||
.split('.')
|
||||
.slice(3)
|
||||
.join('.');
|
||||
if (patchVersion) {
|
||||
return `${mainVersion}+${patchVersion}.${buildVersion}`;
|
||||
}
|
||||
if (buildVersion != 0) {
|
||||
return `${mainVersion}+${buildVersion}`;
|
||||
}
|
||||
|
||||
Vendored
+343
File diff suppressed because one or more lines are too long
Vendored
+2
-2
@@ -8,7 +8,7 @@ export const modules = {
|
||||
/* 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_cache__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4219);
|
||||
/* harmony import */ var _actions_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3838);
|
||||
/* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4527);
|
||||
|
||||
@@ -46,7 +46,7 @@ __webpack_require__.r(__webpack_exports__);
|
||||
/* 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_cache__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4219);
|
||||
/* 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);
|
||||
|
||||
|
||||
Vendored
+161
-44
@@ -7,12 +7,14 @@ export const modules = {
|
||||
|
||||
__webpack_require__.r(__webpack_exports__);
|
||||
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||
/* harmony export */ addMavenServerRepositoryOrigins: () => (/* binding */ addMavenServerRepositoryOrigins),
|
||||
/* harmony export */ configureAuthentication: () => (/* binding */ configureAuthentication),
|
||||
/* harmony export */ createAuthenticationSettings: () => (/* binding */ createAuthenticationSettings),
|
||||
/* harmony export */ generate: () => (/* binding */ generate),
|
||||
/* harmony export */ getInputWithDeprecatedAlias: () => (/* binding */ getInputWithDeprecatedAlias),
|
||||
/* harmony export */ getMavenRepositorySettings: () => (/* binding */ getMavenRepositorySettings),
|
||||
/* harmony export */ getMavenServerSettings: () => (/* binding */ getMavenServerSettings),
|
||||
/* harmony export */ parseMavenMirrors: () => (/* binding */ parseMavenMirrors),
|
||||
/* harmony export */ parseMavenRepositories: () => (/* binding */ parseMavenRepositories),
|
||||
/* harmony export */ parseMavenServerCredentials: () => (/* binding */ parseMavenServerCredentials)
|
||||
/* harmony export */ });
|
||||
@@ -37,9 +39,11 @@ __webpack_require__.r(__webpack_exports__);
|
||||
|
||||
|
||||
|
||||
const MAVEN_REPOSITORY_ORIGIN = /^[A-Za-z][A-Za-z0-9+.-]*:\/\/[^/?#@]+\/?$/;
|
||||
async function configureAuthentication() {
|
||||
const servers = getMavenServerSettings();
|
||||
const repositorySettings = getMavenRepositorySettings();
|
||||
const mirrors = parseMavenMirrors(_actions_core__WEBPACK_IMPORTED_MODULE_1__/* .getMultilineInput */ .q3(_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_MVN_MIRRORS */ .K));
|
||||
const settingsDirectory = _actions_core__WEBPACK_IMPORTED_MODULE_1__/* .getInput */ .V4(_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_SETTINGS_PATH */ .Xh) ||
|
||||
path__WEBPACK_IMPORTED_MODULE_0__.join(os__WEBPACK_IMPORTED_MODULE_4__.homedir(), _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .M2_DIR */ .iT);
|
||||
const overwriteSettings = (0,_util_js__WEBPACK_IMPORTED_MODULE_6__/* .getBooleanInput */ .Vt)(_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_OVERWRITE_SETTINGS */ .TS, true);
|
||||
@@ -49,7 +53,7 @@ async function configureAuthentication() {
|
||||
if (gpgPrivateKey) {
|
||||
_actions_core__WEBPACK_IMPORTED_MODULE_1__/* .setSecret */ .Pq(gpgPrivateKey);
|
||||
}
|
||||
await createAuthenticationSettings(servers, settingsDirectory, overwriteSettings, gpgPassphraseEnvVar, repositorySettings);
|
||||
await createAuthenticationSettings(servers, settingsDirectory, overwriteSettings, gpgPassphraseEnvVar, repositorySettings, mirrors);
|
||||
if (gpgPrivateKey) {
|
||||
_actions_core__WEBPACK_IMPORTED_MODULE_1__/* .info */ .pq('Importing private gpg key');
|
||||
const gpgHome = await _gpg_js__WEBPACK_IMPORTED_MODULE_5__/* .importKey */ .Fh(gpgPrivateKey);
|
||||
@@ -74,16 +78,16 @@ function getInputWithDeprecatedAlias(inputName, deprecatedInputName, defaultValu
|
||||
// only exported for testing purposes
|
||||
function getMavenServerSettings() {
|
||||
const entries = _actions_core__WEBPACK_IMPORTED_MODULE_1__/* .getMultilineInput */ .q3(_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_MVN_SERVER_CREDENTIALS */ .MM);
|
||||
if (entries.some(entry => entry.trim())) {
|
||||
return parseMavenServerCredentials(entries);
|
||||
}
|
||||
return [
|
||||
const servers = entries.some(entry => entry.trim())
|
||||
? parseMavenServerCredentials(entries)
|
||||
: [
|
||||
{
|
||||
id: _actions_core__WEBPACK_IMPORTED_MODULE_1__/* .getInput */ .V4(_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_SERVER_ID */ .fd),
|
||||
usernameEnvVar: getInputWithDeprecatedAlias(_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_SERVER_USERNAME_ENV_VAR */ .sc, _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_SERVER_USERNAME_DEPRECATED */ .sp, _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_DEFAULT_SERVER_USERNAME */ .Wj),
|
||||
passwordEnvVar: getInputWithDeprecatedAlias(_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_SERVER_PASSWORD_ENV_VAR */ .r4, _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_SERVER_PASSWORD_DEPRECATED */ .Vt, _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_DEFAULT_SERVER_PASSWORD */ .xp)
|
||||
}
|
||||
];
|
||||
return addMavenServerRepositoryOrigins(servers, _actions_core__WEBPACK_IMPORTED_MODULE_1__/* .getMultilineInput */ .q3(_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_MVN_SERVER_REPOSITORY_ORIGINS */ .Pt));
|
||||
}
|
||||
// only exported for testing purposes
|
||||
function parseMavenServerCredentials(entries) {
|
||||
@@ -110,20 +114,78 @@ function parseMavenServerCredentials(entries) {
|
||||
return servers;
|
||||
}
|
||||
// only exported for testing purposes
|
||||
function addMavenServerRepositoryOrigins(servers, entries) {
|
||||
const serverIds = new Set(servers.map(server => server.id));
|
||||
const originsByServer = new Map();
|
||||
entries.forEach((entry, index) => {
|
||||
if (!entry.trim()) {
|
||||
return;
|
||||
}
|
||||
const separator = entry.indexOf(':');
|
||||
if (separator <= 0 || separator === entry.length - 1) {
|
||||
throw new Error(`Invalid mvn-server-repository-origins entry at line ${index + 1}. Expected format: server-id:repository-origin`);
|
||||
}
|
||||
const id = entry.slice(0, separator).trim();
|
||||
const value = entry.slice(separator + 1).trim();
|
||||
if (!id || !value) {
|
||||
throw new Error(`Invalid mvn-server-repository-origins entry at line ${index + 1}. Server ID and repository origin are required`);
|
||||
}
|
||||
if (!serverIds.has(id)) {
|
||||
throw new Error(`Unknown server-id '${id}' in mvn-server-repository-origins at line ${index + 1}`);
|
||||
}
|
||||
let url;
|
||||
try {
|
||||
if (!MAVEN_REPOSITORY_ORIGIN.test(value)) {
|
||||
throw new Error();
|
||||
}
|
||||
url = new URL(value);
|
||||
}
|
||||
catch {
|
||||
throw new Error(`Invalid repository origin '${value}' in mvn-server-repository-origins at line ${index + 1}`);
|
||||
}
|
||||
if (!url.host ||
|
||||
url.username ||
|
||||
url.password ||
|
||||
(url.pathname !== '' && url.pathname !== '/') ||
|
||||
url.search ||
|
||||
url.hash) {
|
||||
throw new Error(`Invalid repository origin '${value}' in mvn-server-repository-origins at line ${index + 1}`);
|
||||
}
|
||||
const explicitPort = /:(\d+)\/?$/.exec(value)?.[1];
|
||||
const port = explicitPort ? Number.parseInt(explicitPort, 10) : undefined;
|
||||
const includePort = port !== undefined &&
|
||||
!((url.protocol === 'http:' && port === 80) ||
|
||||
(url.protocol === 'https:' && port === 443));
|
||||
const origin = `${url.protocol}//${url.hostname}${includePort ? `:${port}` : ''}`;
|
||||
const origins = originsByServer.get(id) || [];
|
||||
if (!origins.includes(origin)) {
|
||||
origins.push(origin);
|
||||
originsByServer.set(id, origins);
|
||||
}
|
||||
});
|
||||
return servers.map(server => {
|
||||
const repositoryOrigins = originsByServer.get(server.id);
|
||||
return repositoryOrigins ? { ...server, repositoryOrigins } : server;
|
||||
});
|
||||
}
|
||||
// only exported for testing purposes
|
||||
function getMavenRepositorySettings() {
|
||||
const entries = _actions_core__WEBPACK_IMPORTED_MODULE_1__/* .getMultilineInput */ .q3(_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_MVN_REPOSITORIES */ .W2);
|
||||
if (!entries.some(entry => entry.trim())) {
|
||||
const pluginEntries = _actions_core__WEBPACK_IMPORTED_MODULE_1__/* .getMultilineInput */ .q3(_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_MVN_PLUGIN_REPOSITORIES */ .gR);
|
||||
if (!entries.some(entry => entry.trim()) &&
|
||||
!pluginEntries.some(entry => entry.trim())) {
|
||||
return undefined;
|
||||
}
|
||||
const includeCentral = (0,_util_js__WEBPACK_IMPORTED_MODULE_6__/* .getBooleanInput */ .Vt)(_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_MVN_REPOSITORIES_INCLUDE_CENTRAL */ .H5, true);
|
||||
return {
|
||||
repositories: parseMavenRepositories(entries, includeCentral),
|
||||
pluginRepositories: parseMavenRepositories(pluginEntries, includeCentral, _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_MVN_PLUGIN_REPOSITORIES */ .gR),
|
||||
includeCentral,
|
||||
prioritizeCentral: (0,_util_js__WEBPACK_IMPORTED_MODULE_6__/* .getBooleanInput */ .Vt)(_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_MVN_REPOSITORIES_PRIORITIZE_CENTRAL */ .OT, true)
|
||||
};
|
||||
}
|
||||
// only exported for testing purposes
|
||||
function parseMavenRepositories(entries, includeCentral) {
|
||||
function parseMavenRepositories(entries, includeCentral, inputName = _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_MVN_REPOSITORIES */ .W2) {
|
||||
const repositories = [];
|
||||
const repositoryIds = new Set();
|
||||
entries.forEach((entry, index) => {
|
||||
@@ -133,7 +195,7 @@ function parseMavenRepositories(entries, includeCentral) {
|
||||
const firstSeparator = entry.indexOf(':');
|
||||
const lastSeparator = entry.lastIndexOf(':');
|
||||
if (firstSeparator <= 0 || lastSeparator <= firstSeparator) {
|
||||
throw new Error(`Invalid mvn-repositories entry at line ${index + 1}. Expected format: repository-id:repository-url:snapshots-enabled`);
|
||||
throw new Error(`Invalid ${inputName} entry at line ${index + 1}. Expected format: repository-id:repository-url:snapshots-enabled`);
|
||||
}
|
||||
const id = entry.slice(0, firstSeparator).trim();
|
||||
const url = entry.slice(firstSeparator + 1, lastSeparator).trim();
|
||||
@@ -142,13 +204,13 @@ function parseMavenRepositories(entries, includeCentral) {
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
if (!id || !url || !snapshotsValue) {
|
||||
throw new Error(`Invalid mvn-repositories entry at line ${index + 1}. repository-id, repository URL, and snapshots-enabled are required`);
|
||||
throw new Error(`Invalid ${inputName} entry at line ${index + 1}. repository-id, repository URL, and snapshots-enabled are required`);
|
||||
}
|
||||
if (snapshotsValue !== 'true' && snapshotsValue !== 'false') {
|
||||
throw new Error(`Invalid snapshots-enabled value '${snapshotsValue}' in mvn-repositories entry at line ${index + 1}. Expected true or false`);
|
||||
throw new Error(`Invalid snapshots-enabled value '${snapshotsValue}' in ${inputName} entry at line ${index + 1}. Expected true or false`);
|
||||
}
|
||||
if (repositoryIds.has(id)) {
|
||||
throw new Error(`Duplicate repository-id '${id}' in mvn-repositories input`);
|
||||
throw new Error(`Duplicate repository-id '${id}' in ${inputName} input`);
|
||||
}
|
||||
if (includeCentral && id === _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MAVEN_CENTRAL_REPOSITORY_ID */ .xg) {
|
||||
throw new Error(`Repository-id '${_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MAVEN_CENTRAL_REPOSITORY_ID */ .xg}' is reserved when ${_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .INPUT_MVN_REPOSITORIES_INCLUDE_CENTRAL */ .H5} is enabled`);
|
||||
@@ -162,15 +224,77 @@ function parseMavenRepositories(entries, includeCentral) {
|
||||
});
|
||||
return repositories;
|
||||
}
|
||||
async function createAuthenticationSettings(servers, settingsDirectory, overwriteSettings, gpgPassphraseEnvVar = undefined, repositorySettings = undefined) {
|
||||
// only exported for testing purposes
|
||||
function parseMavenMirrors(entries) {
|
||||
const mirrors = [];
|
||||
const mirrorIds = new Set();
|
||||
entries.forEach((entry, index) => {
|
||||
if (!entry.trim()) {
|
||||
return;
|
||||
}
|
||||
const separator = entry.indexOf(':');
|
||||
const remainder = entry.slice(separator + 1).trim();
|
||||
// Protect scheme/authority colons (including IPv6 and ports) before
|
||||
// finding the selector, which may itself contain external:http:*.
|
||||
const urlPrefix = /^[A-Za-z][A-Za-z0-9+.-]*:(?:\/\/(?:[^/?#@\s]*@)?(?:\[[^\]]+\]|[^:/?#\s]+)(?::\d+(?=[/?#:\s]|$))?)?/.exec(remainder)?.[0] ?? '';
|
||||
const fields = /^(.*?):((?:\s*!?external:(?:http:)?\*\s*|[^:,]*)(?:,(?:\s*!?external:(?:http:)?\*\s*|[^:,]*))*)$/.exec(remainder.slice(urlPrefix.length));
|
||||
if (separator < 0 || !fields) {
|
||||
throw new Error(`Invalid mvn-mirrors entry at line ${index + 1}. Expected format: mirror-id:mirror-url:mirror-of`);
|
||||
}
|
||||
const id = entry.slice(0, separator).trim();
|
||||
const url = `${urlPrefix}${fields[1]}`.trim();
|
||||
const mirrorOf = fields[2].trim();
|
||||
if (!id || !url || !mirrorOf) {
|
||||
throw new Error(`Invalid mvn-mirrors entry at line ${index + 1}. mirror-id, mirror URL, and mirror-of are required`);
|
||||
}
|
||||
if (mirrorIds.has(id)) {
|
||||
throw new Error(`Duplicate mirror-id '${id}' in mvn-mirrors input at line ${index + 1}`);
|
||||
}
|
||||
mirrorIds.add(id);
|
||||
mirrors.push({ id, url, mirrorOf });
|
||||
});
|
||||
return mirrors;
|
||||
}
|
||||
function generateRepositorySection(customRepositories, settings, element) {
|
||||
if (!customRepositories.length && settings.includeCentral) {
|
||||
return [];
|
||||
}
|
||||
const centralRepository = {
|
||||
id: _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MAVEN_CENTRAL_REPOSITORY_ID */ .xg,
|
||||
url: _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MAVEN_CENTRAL_REPOSITORY_URL */ .jv,
|
||||
snapshotsEnabled: false
|
||||
};
|
||||
const customCentralConfigured = customRepositories.some(repository => repository.id === _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MAVEN_CENTRAL_REPOSITORY_ID */ .xg);
|
||||
const repositories = settings.includeCentral
|
||||
? settings.prioritizeCentral
|
||||
? [centralRepository, ...customRepositories]
|
||||
: [...customRepositories, centralRepository]
|
||||
: customCentralConfigured
|
||||
? customRepositories
|
||||
: [...customRepositories, { ...centralRepository, releasesEnabled: false }];
|
||||
const container = element === 'repository' ? 'repositories' : 'pluginRepositories';
|
||||
const lines = [` <${container}>`];
|
||||
for (const repository of repositories) {
|
||||
lines.push(` <${element}>`, ` <id>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(repository.id)}</id>`, ` <url>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(repository.url)}</url>`, ...(repository.releasesEnabled === undefined
|
||||
? []
|
||||
: [
|
||||
' <releases>',
|
||||
` <enabled>${repository.releasesEnabled}</enabled>`,
|
||||
' </releases>'
|
||||
]), ' <snapshots>', ` <enabled>${repository.snapshotsEnabled}</enabled>`, ' </snapshots>', ` </${element}>`);
|
||||
}
|
||||
lines.push(` </${container}>`);
|
||||
return lines;
|
||||
}
|
||||
async function createAuthenticationSettings(servers, settingsDirectory, overwriteSettings, gpgPassphraseEnvVar = undefined, repositorySettings = undefined, mirrors = []) {
|
||||
_actions_core__WEBPACK_IMPORTED_MODULE_1__/* .info */ .pq(`Creating ${_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MVN_SETTINGS_FILE */ .vO} with server-id: ${servers.map(server => server.id).join(', ')}`);
|
||||
// when an alternate m2 location is specified use only that location (no .m2 directory)
|
||||
// otherwise use the home/.m2/ path
|
||||
await _actions_io__WEBPACK_IMPORTED_MODULE_2__/* .mkdirP */ .U$(settingsDirectory);
|
||||
await write(settingsDirectory, generate(servers, gpgPassphraseEnvVar, repositorySettings), overwriteSettings);
|
||||
await write(settingsDirectory, generate(servers, gpgPassphraseEnvVar, repositorySettings, mirrors), overwriteSettings);
|
||||
}
|
||||
// only exported for testing purposes
|
||||
function generate(servers, gpgPassphraseEnvVar, repositorySettings) {
|
||||
function generate(servers, gpgPassphraseEnvVar, repositorySettings, mirrors = []) {
|
||||
// The maven-gpg-plugin reads the passphrase from the environment variable
|
||||
// named by the `gpg.passphraseEnvName` property (default MAVEN_GPG_PASSPHRASE).
|
||||
// Only configure it when the requested env var name differs from that default;
|
||||
@@ -187,39 +311,28 @@ function generate(servers, gpgPassphraseEnvVar, repositorySettings) {
|
||||
' <servers>'
|
||||
];
|
||||
for (const server of servers) {
|
||||
lines.push(' <server>', ` <id>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(server.id)}</id>`, ` <username>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(`\${env.${server.usernameEnvVar}}`)}</username>`, ` <password>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(`\${env.${server.passwordEnvVar}}`)}</password>`, ' </server>');
|
||||
lines.push(' <server>', ` <id>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(server.id)}</id>`, ` <username>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(`\${env.${server.usernameEnvVar}}`)}</username>`, ` <password>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(`\${env.${server.passwordEnvVar}}`)}</password>`);
|
||||
if (server.repositoryOrigins) {
|
||||
lines.push(' <repositoryOrigins>');
|
||||
for (const origin of server.repositoryOrigins) {
|
||||
lines.push(` <repositoryOrigin>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(origin)}</repositoryOrigin>`);
|
||||
}
|
||||
lines.push(' </repositoryOrigins>');
|
||||
}
|
||||
lines.push(' </server>');
|
||||
}
|
||||
lines.push(' </servers>');
|
||||
if (mirrors.length) {
|
||||
lines.push(' <mirrors>');
|
||||
for (const mirror of mirrors) {
|
||||
lines.push(' <mirror>', ` <id>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(mirror.id)}</id>`, ` <url>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(mirror.url)}</url>`, ` <mirrorOf>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(mirror.mirrorOf)}</mirrorOf>`, ' </mirror>');
|
||||
}
|
||||
lines.push(' </mirrors>');
|
||||
}
|
||||
if (repositorySettings || includeGpgPassphraseProfile) {
|
||||
lines.push(' <profiles>');
|
||||
if (repositorySettings) {
|
||||
const centralRepository = {
|
||||
id: _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MAVEN_CENTRAL_REPOSITORY_ID */ .xg,
|
||||
url: _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MAVEN_CENTRAL_REPOSITORY_URL */ .jv,
|
||||
snapshotsEnabled: false
|
||||
};
|
||||
const customCentralConfigured = repositorySettings.repositories.some(repository => repository.id === _constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MAVEN_CENTRAL_REPOSITORY_ID */ .xg);
|
||||
const repositories = repositorySettings.includeCentral
|
||||
? repositorySettings.prioritizeCentral
|
||||
? [centralRepository, ...repositorySettings.repositories]
|
||||
: [...repositorySettings.repositories, centralRepository]
|
||||
: customCentralConfigured
|
||||
? repositorySettings.repositories
|
||||
: [
|
||||
...repositorySettings.repositories,
|
||||
{ ...centralRepository, releasesEnabled: false }
|
||||
];
|
||||
lines.push(' <profile>', ` <id>${_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MAVEN_REPOSITORIES_PROFILE_ID */ .hq}</id>`, ' <repositories>');
|
||||
for (const repository of repositories) {
|
||||
lines.push(' <repository>', ` <id>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(repository.id)}</id>`, ` <url>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(repository.url)}</url>`, ...(repository.releasesEnabled === undefined
|
||||
? []
|
||||
: [
|
||||
' <releases>',
|
||||
` <enabled>${repository.releasesEnabled}</enabled>`,
|
||||
' </releases>'
|
||||
]), ' <snapshots>', ` <enabled>${repository.snapshotsEnabled}</enabled>`, ' </snapshots>', ' </repository>');
|
||||
}
|
||||
lines.push(' </repositories>', ' </profile>');
|
||||
lines.push(' <profile>', ` <id>${_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .MAVEN_REPOSITORIES_PROFILE_ID */ .hq}</id>`, ...generateRepositorySection(repositorySettings.repositories, repositorySettings, 'repository'), ...generateRepositorySection(repositorySettings.pluginRepositories ?? [], repositorySettings, 'pluginRepository'), ' </profile>');
|
||||
}
|
||||
if (includeGpgPassphraseProfile) {
|
||||
lines.push(' <profile>', ` <id>${_constants_js__WEBPACK_IMPORTED_MODULE_7__/* .GPG_PASSPHRASE_PROFILE_ID */ .K$}</id>`, ' <properties>', ` <gpg.passphraseEnvName>${(0,_xml_js__WEBPACK_IMPORTED_MODULE_8__/* .escapeXmlText */ .I)(gpgPassphraseEnvVar)}</gpg.passphraseEnvName>`, ' </properties>', ' </profile>');
|
||||
@@ -350,13 +463,16 @@ async function removeGpgHome(gpgHome) {
|
||||
if (!fs__WEBPACK_IMPORTED_MODULE_0__.existsSync(resolvedGpgHome)) {
|
||||
return;
|
||||
}
|
||||
await stopGpgAgent(resolvedGpgHome);
|
||||
await _actions_io__WEBPACK_IMPORTED_MODULE_3__/* .rmRF */ .Yz(resolvedGpgHome);
|
||||
}
|
||||
async function stopGpgAgent(gpgHome) {
|
||||
try {
|
||||
await _actions_exec__WEBPACK_IMPORTED_MODULE_4__/* .exec */ .m('gpgconf', ['--homedir', toGpgPath(resolvedGpgHome), '--kill', 'gpg-agent'], { silent: true, ignoreReturnCode: true });
|
||||
await _actions_exec__WEBPACK_IMPORTED_MODULE_4__/* .exec */ .m('gpgconf', ['--homedir', toGpgPath(gpgHome), '--kill', 'gpg-agent'], { silent: true, ignoreReturnCode: true });
|
||||
}
|
||||
catch {
|
||||
// gpgconf may be unavailable, but directory removal must still be attempted.
|
||||
}
|
||||
await _actions_io__WEBPACK_IMPORTED_MODULE_3__/* .rmRF */ .Yz(resolvedGpgHome);
|
||||
}
|
||||
async function verifyPackageSignature(archivePath, signatureUrl, publicKeyContent) {
|
||||
const signaturePath = await _actions_tool_cache__WEBPACK_IMPORTED_MODULE_5__/* .downloadTool */ .bq(signatureUrl);
|
||||
@@ -402,6 +518,7 @@ async function verifyPackageSignature(archivePath, signatureUrl, publicKeyConten
|
||||
], options);
|
||||
}
|
||||
finally {
|
||||
await stopGpgAgent(gpgHome);
|
||||
await _actions_io__WEBPACK_IMPORTED_MODULE_3__/* .rmRF */ .Yz(signaturePath);
|
||||
await _actions_io__WEBPACK_IMPORTED_MODULE_3__/* .rmRF */ .Yz(gpgHome);
|
||||
}
|
||||
|
||||
Vendored
+242
-28
@@ -10,7 +10,7 @@ export const modules = {
|
||||
/* harmony export */ i: () => (/* binding */ XMLValidator)
|
||||
/* harmony export */ });
|
||||
/* harmony import */ var _validator_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1176);
|
||||
/* harmony import */ var _xmlparser_XMLParser_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6009);
|
||||
/* harmony import */ var _xmlparser_XMLParser_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1143);
|
||||
|
||||
|
||||
|
||||
@@ -418,9 +418,102 @@ function readAttributeStr(xmlData, i) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Select all the attributes whether valid or invalid.
|
||||
* Walk `attrStr` once, left to right, splitting it into attribute tokens.
|
||||
*
|
||||
* This replaces a regex that used to do the same job
|
||||
* (`(\s*)([^\s=]+)(\s*=)?(\s*(['"])(([\s\S])*?)\5)?`). That regex led with an
|
||||
* optional whitespace group followed by a required "non-whitespace" group.
|
||||
* On a long run of whitespace that never resolves into an attribute name
|
||||
* (e.g. a tag with thousands of trailing spaces before `>`), the engine
|
||||
* backtracks the whitespace group one character at a time before giving up
|
||||
* and moving to the next starting position — one full backtrack per
|
||||
* position, which is quadratic in the length of the run.
|
||||
*
|
||||
* A single forward-only scan can never backtrack, so it can't be made slow
|
||||
* this way no matter how much whitespace the input contains — it's always
|
||||
* proportional to the length of the string, once.
|
||||
*
|
||||
* Each returned token mirrors the shape the old regex match array had, so
|
||||
* the validation logic below (which reads token[1]..token[6]) didn't need
|
||||
* to change:
|
||||
* token.startIndex - where this token begins in attrStr
|
||||
* token[1] - leading whitespace before the name
|
||||
* token[2] - the attribute name
|
||||
* token[3] - whitespace + '=' if present, else undefined
|
||||
* token[4] - marker (any defined value) if a quoted value was found
|
||||
* token[5] - the quote character used ('"' or "'")
|
||||
* token[6] - the value's text, without the surrounding quotes
|
||||
*
|
||||
* A malformed leading character (e.g. a stray '=' with no name before it)
|
||||
* is simply skipped over, one character at a time — the same outcome the
|
||||
* old regex produced by failing to match at that position and retrying at
|
||||
* the next one.
|
||||
*/
|
||||
const validAttrStrRegxp = new RegExp('(\\s*)([^\\s=]+)(\\s*=)?(\\s*([\'"])(([\\s\\S])*?)\\5)?', 'g');
|
||||
function scanAttributeTokens(attrStr) {
|
||||
const tokens = [];
|
||||
const len = attrStr.length;
|
||||
let i = 0;
|
||||
|
||||
while (i < len) {
|
||||
const tokenStart = i;
|
||||
|
||||
// Leading whitespace before the name.
|
||||
while (i < len && isWhiteSpace(attrStr[i])) i++;
|
||||
if (i >= len) break; // trailing whitespace only — nothing left to read
|
||||
|
||||
if (attrStr[i] === '=') {
|
||||
// No name before this '=' — not a valid attribute start. Move past
|
||||
// just this one character and try again from the next position.
|
||||
i = tokenStart + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
const leadingWs = attrStr.slice(tokenStart, i);
|
||||
|
||||
// Attribute name — everything up to the next whitespace or '='.
|
||||
const nameStart = i;
|
||||
while (i < len && !isWhiteSpace(attrStr[i]) && attrStr[i] !== '=') i++;
|
||||
const name = attrStr.slice(nameStart, i);
|
||||
|
||||
// Optional whitespace + '='.
|
||||
let equalsGroup; // whitespace + '=' text, or undefined if absent
|
||||
let j = i;
|
||||
while (j < len && isWhiteSpace(attrStr[j])) j++;
|
||||
if (j < len && attrStr[j] === '=') {
|
||||
equalsGroup = attrStr.slice(i, j + 1);
|
||||
i = j + 1;
|
||||
}
|
||||
|
||||
// Optional whitespace + quoted value.
|
||||
let quoteChar;
|
||||
let value;
|
||||
let k = i;
|
||||
while (k < len && isWhiteSpace(attrStr[k])) k++;
|
||||
if (k < len && (attrStr[k] === '"' || attrStr[k] === "'")) {
|
||||
const valueStart = k + 1;
|
||||
const closeIdx = attrStr.indexOf(attrStr[k], valueStart);
|
||||
if (closeIdx !== -1) {
|
||||
quoteChar = attrStr[k];
|
||||
value = attrStr.slice(valueStart, closeIdx);
|
||||
i = closeIdx + 1;
|
||||
}
|
||||
// No closing quote found anywhere in the rest of the string — leave
|
||||
// quoteChar/value undefined, same as the old regex's group failing
|
||||
// to match a backreference-less run.
|
||||
}
|
||||
|
||||
const token = { startIndex: tokenStart };
|
||||
token[1] = leadingWs;
|
||||
token[2] = name;
|
||||
token[3] = equalsGroup;
|
||||
token[4] = quoteChar !== undefined ? true : undefined;
|
||||
token[5] = quoteChar;
|
||||
token[6] = value;
|
||||
tokens.push(token);
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
//attr, ="sd", a="amit's", a="sd"b="saf", ab cd=""
|
||||
|
||||
@@ -429,7 +522,7 @@ function validateAttributeString(attrStr, options) {
|
||||
|
||||
//if(attrStr.trim().length === 0) return true; //empty string
|
||||
|
||||
const matches = (0,_util_js__WEBPACK_IMPORTED_MODULE_0__/* .getAllMatches */ .Xe)(attrStr, validAttrStrRegxp);
|
||||
const matches = scanAttributeTokens(attrStr);
|
||||
const attrNames = {};
|
||||
|
||||
for (let i = 0; i < matches.length; i++) {
|
||||
@@ -532,10 +625,9 @@ function getPositionFromMatch(match) {
|
||||
return match.startIndex + match[1].length;
|
||||
}
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 6009:
|
||||
/***/ 1143:
|
||||
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
||||
|
||||
|
||||
@@ -1490,8 +1582,10 @@ function toNumber(str, options = {}) {
|
||||
return parse_int(trimmedStr, 2);
|
||||
} else if (options.octal && octRegex.test(trimmedStr)) {
|
||||
return parse_int(trimmedStr, 8);
|
||||
} else if (!isFinite(trimmedStr)) { //Infinity
|
||||
return handleInfinity(str, Number(trimmedStr), options);
|
||||
} else if (!isFinite(trimmedStr)) { //Infinity or not a number
|
||||
const num = Number(trimmedStr);
|
||||
if (Number.isNaN(num)) return str; //not a number: always the original string
|
||||
return handleInfinity(str, num, options); //only +/-Infinity reaches here
|
||||
} else if (trimmedStr.includes('e') || trimmedStr.includes('E')) { //eNotation
|
||||
return resolveEnotation(str, trimmedStr, options);
|
||||
} else {
|
||||
@@ -1503,8 +1597,8 @@ function toNumber(str, options = {}) {
|
||||
const leadingZeros = match[2];
|
||||
let numTrimmedByZeros = trimZeros(match[3]); //complete num without leading zeros
|
||||
const decimalAdjacentToLeadingZeros = sign ? // 0., -00., 000.
|
||||
str[leadingZeros.length + 1] === "."
|
||||
: str[leadingZeros.length] === ".";
|
||||
trimmedStr[leadingZeros.length + 1] === "."
|
||||
: trimmedStr[leadingZeros.length] === ".";
|
||||
|
||||
//trim ending zeros for floating number
|
||||
if (!options.leadingZeros //leading zeros are not allowed
|
||||
@@ -1552,8 +1646,8 @@ function resolveEnotation(str, trimmedStr, options) {
|
||||
const eChar = notation[3].indexOf("e") === -1 ? "E" : "e";
|
||||
const leadingZeros = notation[2];
|
||||
const eAdjacentToLeadingZeros = sign ? // 0E.
|
||||
str[leadingZeros.length + 1] === eChar
|
||||
: str[leadingZeros.length] === eChar;
|
||||
trimmedStr[leadingZeros.length + 1] === eChar
|
||||
: trimmedStr[leadingZeros.length] === eChar;
|
||||
|
||||
if (leadingZeros.length > 1 && eAdjacentToLeadingZeros) return str;
|
||||
else if (leadingZeros.length === 1
|
||||
@@ -1562,8 +1656,7 @@ function resolveEnotation(str, trimmedStr, options) {
|
||||
} else if (leadingZeros.length > 0) {
|
||||
// Has leading zeros — only accept if leadingZeros option allows it
|
||||
if (options.leadingZeros && !eAdjacentToLeadingZeros) {
|
||||
trimmedStr = (notation[1] || "") + notation[3];
|
||||
return Number(trimmedStr);
|
||||
return Number(trimmedStr); // Number() accepts leading zeros; stripping can leave a mantissa-less ".e0"
|
||||
} else return str;
|
||||
} else {
|
||||
// No leading zeros — always valid e-notation, parse it
|
||||
@@ -1626,6 +1719,7 @@ function handleInfinity(str, num, options) {
|
||||
return str; // Return original string like "1e1000"
|
||||
}
|
||||
}
|
||||
|
||||
;// CONCATENATED MODULE: ./node_modules/fast-xml-parser/src/ignoreAttributes.js
|
||||
function getIgnoreAttributesFn(ignoreAttributes) {
|
||||
if (typeof ignoreAttributes === 'function') {
|
||||
@@ -3807,6 +3901,56 @@ const SQL_STRICT_PATTERNS = [...sql, ...SQL_STRICT_EXTRA];
|
||||
|
||||
/* harmony default export */ const sql_strict = (SQL_STRICT_PATTERNS);
|
||||
|
||||
;// CONCATENATED MODULE: ./node_modules/is-unsafe/src/util/decode-entities.js
|
||||
/**
|
||||
* Decode the HTML character references that matter for markup-context
|
||||
* detection.
|
||||
*
|
||||
* Browsers decode character references in attribute values before they
|
||||
* interpret a URL, so `javascript:alert(1)` in an href is the
|
||||
* `javascript:` scheme. The HTML/XML/SVG rules match literal characters, so
|
||||
* without this step such payloads slip through.
|
||||
*
|
||||
* Deliberately NOT a full entity decoder. It decodes only:
|
||||
* - decimal refs : : : : (any leading zeros,
|
||||
* trailing ; optional — browsers accept both)
|
||||
* - hex refs : : : : (same)
|
||||
* - named refs : 	 

|
||||
* Named refs are CASE-SENSITIVE and REQUIRE the trailing ';', exactly
|
||||
* like a browser (so `&COLON;` and `&colon` are not decoded).
|
||||
* 	 / 
 matter because the URL parser strips tab/newline,
|
||||
* so `java	script:` is still a javascript: URL.
|
||||
*
|
||||
* It does NOT decode & (single pass only), so double-encoded text such as
|
||||
* "&lt;script&gt;" stays literal — a browser renders that as text.
|
||||
*
|
||||
* All quantifiers are bounded; no ReDoS risk.
|
||||
*
|
||||
* @param {string} input
|
||||
* @returns {string}
|
||||
*/
|
||||
const NAMED = Object.freeze({
|
||||
colon: ':',
|
||||
Tab: '\t',
|
||||
NewLine: '\n',
|
||||
});
|
||||
|
||||
const ENTITY_RE =
|
||||
/&(?:#[xX]0*([0-9a-fA-F]{1,6});?|#0*([0-9]{1,7});?|([a-zA-Z][a-zA-Z0-9]{1,15});)/g;
|
||||
|
||||
function decodeEntities(input) {
|
||||
if (input.indexOf('&') === -1) return input;
|
||||
|
||||
return input.replace(ENTITY_RE, (match, hex, dec, name) => {
|
||||
if (name !== undefined) {
|
||||
return Object.prototype.hasOwnProperty.call(NAMED, name) ? NAMED[name] : match;
|
||||
}
|
||||
const cp = hex !== undefined ? parseInt(hex, 16) : parseInt(dec, 10);
|
||||
if (!Number.isFinite(cp) || cp > 0x10ffff) return match;
|
||||
return String.fromCodePoint(cp);
|
||||
});
|
||||
}
|
||||
|
||||
;// CONCATENATED MODULE: ./node_modules/is-unsafe/src/contexts/html.js
|
||||
/**
|
||||
* HTML context patterns.
|
||||
@@ -3970,7 +4114,8 @@ const XML_PATTERNS = [
|
||||
{
|
||||
id: 'xml-namespace-confusion',
|
||||
description: 'xmlns: attribute injection — can redefine namespaces to confuse parsers',
|
||||
pattern: /\bxmlns\s*(?::\w{1,40})?\s*=/i,
|
||||
// pattern: /\bxmlns\s*(?::\w{1,40})?\s*=/i,
|
||||
pattern: /\bxmlns(?::\w{1,40})?\s*=/i,
|
||||
},
|
||||
{
|
||||
id: 'xml-comment-injection',
|
||||
@@ -4489,6 +4634,11 @@ const LOG_PATTERNS = [
|
||||
|
||||
|
||||
|
||||
// Markup contexts are also matched against an entity-decoded copy of the input,
|
||||
// because browsers decode HTML character references before interpreting a URL
|
||||
// scheme (e.g. javascript:… is a javascript: URL).
|
||||
|
||||
|
||||
// ─── VALID_CONTEXTS convenience re-export ─────────────────────────────────
|
||||
// Importing this pulls in ALL contexts. Use it only when you need all of them
|
||||
// (e.g. for validation UI, tooling, or exhaustive audits).
|
||||
@@ -4511,6 +4661,12 @@ const LOG_PATTERNS = [
|
||||
html.label = 'HTML';
|
||||
xml.label = 'XML';
|
||||
svg.label = 'SVG';
|
||||
|
||||
// Markup contexts: also test an entity-decoded copy (see util/decode-entities.js).
|
||||
// Non-markup contexts (SQL, SHELL, LOG, …) are matched byte-for-byte as supplied.
|
||||
html.decodeEntities = true;
|
||||
xml.decodeEntities = true;
|
||||
svg.decodeEntities = true;
|
||||
sql.label = 'SQL';
|
||||
sql_strict.label = 'SQL-STRICT';
|
||||
shell.label = 'SHELL';
|
||||
@@ -4605,6 +4761,20 @@ function normalise(context) {
|
||||
return { lists: [context], regex: null };
|
||||
}
|
||||
|
||||
/**
|
||||
* For markup contexts, return an entity-decoded copy of `value` to test
|
||||
* alongside the raw input, or null when there is nothing extra to test.
|
||||
*
|
||||
* @param {string} value
|
||||
* @param {Rule[]} list
|
||||
* @returns {string|null}
|
||||
*/
|
||||
function decodedCopy(value, list) {
|
||||
if (!list.decodeEntities) return null;
|
||||
const decoded = decodeEntities(value);
|
||||
return decoded === value ? null : decoded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test value against a single PatternList. Returns the first MatchResult or null.
|
||||
*
|
||||
@@ -4614,8 +4784,9 @@ function normalise(context) {
|
||||
*/
|
||||
function matchList(value, list) {
|
||||
const label = list.label ?? 'CUSTOM';
|
||||
const decoded = decodedCopy(value, list);
|
||||
for (const rule of list) {
|
||||
if (rule.pattern.test(value)) {
|
||||
if (rule.pattern.test(value) || (decoded !== null && rule.pattern.test(decoded))) {
|
||||
return { context: label, id: rule.id, description: rule.description, pattern: rule.pattern };
|
||||
}
|
||||
}
|
||||
@@ -4713,8 +4884,9 @@ function allUnsafe(value, context) {
|
||||
|
||||
for (const list of lists) {
|
||||
const label = list.label ?? 'CUSTOM';
|
||||
const decoded = decodedCopy(value, list);
|
||||
for (const rule of list) {
|
||||
if (rule.pattern.test(value)) {
|
||||
if (rule.pattern.test(value) || (decoded !== null && rule.pattern.test(decoded))) {
|
||||
results.push({ context: label, id: rule.id, description: rule.description, pattern: rule.pattern });
|
||||
}
|
||||
}
|
||||
@@ -5012,7 +5184,15 @@ function buildAttributesMap(attrStr, jPath, tagName, force = false) {
|
||||
}
|
||||
}
|
||||
const parseXml = function (xmlData) {
|
||||
xmlData = xmlData.replace(/\r\n?/g, "\n"); //TODO: remove this line
|
||||
const options = this.options;
|
||||
const crlfPositions = options.captureMetaData ? [] : undefined;
|
||||
xmlData = xmlData.replace(/\r\n?/g, options.captureMetaData ? (newline, index) => {
|
||||
if (newline.length === 2) {
|
||||
// Store the position after earlier CRLF pairs have been normalized.
|
||||
crlfPositions.push(index - crlfPositions.length);
|
||||
}
|
||||
return "\n";
|
||||
} : "\n");
|
||||
const xmlObj = new XmlNode('!xml');
|
||||
let currentNode = xmlObj;
|
||||
let textData = "";
|
||||
@@ -5025,7 +5205,6 @@ const parseXml = function (xmlData) {
|
||||
this.entityExpansionCount = 0;
|
||||
this.currentExpandedLength = 0;
|
||||
this.doctypefound = false;
|
||||
const options = this.options;
|
||||
const docTypeReader = new DocTypeReader(options.processEntities);
|
||||
const xmlLen = xmlData.length;
|
||||
for (let i = 0; i < xmlLen; i++) {//for each char in XML data
|
||||
@@ -5069,7 +5248,7 @@ const parseXml = function (xmlData) {
|
||||
currentNode = this.tagsNodeStack.pop() || xmlObj;//avoid recursion, set the parent tag scope
|
||||
|
||||
if (options.captureMetaData && currentNode) {
|
||||
currentNode.addEndIndex(closeIndex + 1);
|
||||
currentNode.addEndIndex(getOriginalIndex(closeIndex + 1, crlfPositions));
|
||||
}
|
||||
textData = "";
|
||||
i = closeIndex;
|
||||
@@ -5095,11 +5274,12 @@ const parseXml = function (xmlData) {
|
||||
if (tagData.tagName !== tagData.tagExp && tagData.attrExpPresent && options.ignoreAttributes !== true) {
|
||||
childNode[":@"] = attsMap
|
||||
}
|
||||
this.addChild(currentNode, childNode, this.readonlyMatcher, i);
|
||||
const startIndex = options.captureMetaData ? getOriginalIndex(i, crlfPositions) : i;
|
||||
this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
|
||||
|
||||
if (options.captureMetaData) {
|
||||
// closeIndex points at '?' of the closing '?>'
|
||||
currentNode.addEndIndex(tagData.closeIndex + 2);
|
||||
currentNode.addEndIndex(getOriginalIndex(tagData.closeIndex + 2, crlfPositions));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5231,7 +5411,7 @@ const parseXml = function (xmlData) {
|
||||
this.isCurrentNodeStopNode = this.isItStopNode();
|
||||
}
|
||||
|
||||
const startIndex = i;
|
||||
const startIndex = options.captureMetaData ? getOriginalIndex(i, crlfPositions) : i;
|
||||
if (this.isCurrentNodeStopNode) {
|
||||
let tagContent = "";
|
||||
|
||||
@@ -5267,7 +5447,7 @@ const parseXml = function (xmlData) {
|
||||
this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
|
||||
|
||||
if (options.captureMetaData) {
|
||||
currentNode.addEndIndex(i + 1);
|
||||
currentNode.addEndIndex(getOriginalIndex(i + 1, crlfPositions));
|
||||
}
|
||||
} else {
|
||||
//selfClosing tag
|
||||
@@ -5281,7 +5461,7 @@ const parseXml = function (xmlData) {
|
||||
this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
|
||||
|
||||
if (options.captureMetaData) {
|
||||
currentNode.addEndIndex(closeIndex + 1);
|
||||
currentNode.addEndIndex(getOriginalIndex(closeIndex + 1, crlfPositions));
|
||||
}
|
||||
this.matcher.pop(); // Pop self-closing tag
|
||||
this.isCurrentNodeStopNode = false; // Reset flag
|
||||
@@ -5294,7 +5474,7 @@ const parseXml = function (xmlData) {
|
||||
this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
|
||||
|
||||
if (options.captureMetaData) {
|
||||
currentNode.addEndIndex(result.closeIndex + 1);
|
||||
currentNode.addEndIndex(getOriginalIndex(result.closeIndex + 1, crlfPositions));
|
||||
}
|
||||
this.matcher.pop(); // Pop unpaired tag
|
||||
this.isCurrentNodeStopNode = false; // Reset flag
|
||||
@@ -5327,6 +5507,17 @@ const parseXml = function (xmlData) {
|
||||
return xmlObj.child;
|
||||
}
|
||||
|
||||
function getOriginalIndex(index, crlfPositions) {
|
||||
let low = 0;
|
||||
let high = crlfPositions.length;
|
||||
while (low < high) {
|
||||
const middle = Math.floor((low + high) / 2);
|
||||
if (crlfPositions[middle] < index) low = middle + 1;
|
||||
else high = middle;
|
||||
}
|
||||
return index + low;
|
||||
}
|
||||
|
||||
function addChild(currentNode, childNode, matcher, startIndex) {
|
||||
// unset startIndex if not requested
|
||||
if (!this.options.captureMetaData) startIndex = undefined;
|
||||
@@ -5505,6 +5696,8 @@ function readStopNodeData(xmlData, tagName, i) {
|
||||
const startIndex = i;
|
||||
// Starting at 1 since we already have an open tag
|
||||
let openTagCount = 1;
|
||||
let lenient = false; // set once a quote-aware tag scan fails
|
||||
let nextGt = -1; // cached index of the next '>'
|
||||
|
||||
const xmllen = xmlData.length;
|
||||
for (; i < xmllen; i++) {
|
||||
@@ -5536,14 +5729,29 @@ function readStopNodeData(xmlData, tagName, i) {
|
||||
const closeIndex = findClosingIndex(xmlData, "]]>", i, "StopNode is not closed.") - 2;
|
||||
i = closeIndex;
|
||||
} else {
|
||||
const tagData = readTagExp(xmlData, i, false)
|
||||
const tagData = lenient ? undefined : readTagExp(xmlData, i, false);
|
||||
|
||||
if (tagData) {
|
||||
const openTagName = tagData && tagData.tagName;
|
||||
const openTagName = tagData.tagName;
|
||||
if (openTagName === tagName && tagData.tagExp[tagData.tagExp.length - 1] !== "/") {
|
||||
openTagCount++;
|
||||
}
|
||||
i = tagData.closeIndex;
|
||||
} else {
|
||||
// no closing '>'. use lenient approach to find '<' instead of readTagExp, to avoid repeated rescans.
|
||||
lenient = true;
|
||||
if (nextGt < i) {
|
||||
nextGt = xmlData.indexOf(">", i);
|
||||
if (nextGt === -1) return; // no '>' left, so no closing tag can follow
|
||||
}
|
||||
const nextLt = xmlData.indexOf("<", i + 1);
|
||||
if (nextLt === -1 || nextLt > nextGt) { // looks like a real tag
|
||||
const exp = xmlData.substring(i + 1, nextGt);
|
||||
const sep = exp.search(/\s/);
|
||||
const name = sep === -1 ? exp : exp.substring(0, sep);
|
||||
if (name === tagName && exp[exp.length - 1] !== "/") openTagCount++;
|
||||
i = nextGt;
|
||||
} // else: treat this '<' as plain text
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5779,6 +5987,7 @@ function isLeafTag(obj, options) {
|
||||
// EXTERNAL MODULE: ./node_modules/fast-xml-parser/src/validator.js
|
||||
var validator = __webpack_require__(1176);
|
||||
;// CONCATENATED MODULE: ./node_modules/fast-xml-parser/src/xmlparser/XMLParser.js
|
||||
/* global Uint8Array */
|
||||
|
||||
|
||||
|
||||
@@ -5799,7 +6008,12 @@ class XMLParser {
|
||||
*/
|
||||
parse(xmlData, validationOption) {
|
||||
if (typeof xmlData !== "string" && xmlData.toString) {
|
||||
if (xmlData instanceof Uint8Array &&
|
||||
!(typeof Buffer !== "undefined" && Buffer.isBuffer(xmlData))) {
|
||||
xmlData = new TextDecoder("utf-8", { ignoreBOM: true }).decode(xmlData);
|
||||
} else {
|
||||
xmlData = xmlData.toString();
|
||||
}
|
||||
} else if (typeof xmlData !== "string") {
|
||||
throw new Error("XML data is accepted in String or Bytes[] form.")
|
||||
}
|
||||
|
||||
Vendored
+62
-18
@@ -52431,6 +52431,24 @@ const EXPANSION_MAX = 100_000;
|
||||
// realistic expansion (100k results hitting `EXPANSION_MAX` measure ~1M
|
||||
// characters) so legitimate input is unaffected.
|
||||
const EXPANSION_MAX_LENGTH = 4_000_000;
|
||||
// `expand_` recurses once per level of brace *nesting* - both when expanding a
|
||||
// set's comma members and when re-wrapping a set whose body is a single part.
|
||||
// The CVE-2026-14257 fix made the *tail* iterative (recursion on `m.post`, one
|
||||
// level per chained group), which left nesting depth unbounded: about 3,100
|
||||
// levels of `{{{...a,b...}}}` - only ~6KB of input - exhausted the native stack
|
||||
// and crashed the process. `EXPANSION_MAX_DEPTH` bounds how deep the parser
|
||||
// will follow nesting. It sits far above any realistic pattern and well below
|
||||
// the depth at which the stack runs out.
|
||||
const EXPANSION_MAX_DEPTH = 1_000;
|
||||
// Bash keeps a quirk where a brace group followed by a comma set still expands
|
||||
// (`{a},b}`). The parser implements it by rewriting the string and restarting
|
||||
// the scan, absorbing one `}` per pass. `n` trailing braces therefore cost `n`
|
||||
// full passes over a string that itself grows by one `escClose` sentinel each
|
||||
// time - quadratic in `n`, with a ~26x constant from the sentinel's length.
|
||||
// 128KB of `'{a}' + '}'.repeat(n) + ',z}'` blocked the event loop for 27
|
||||
// seconds to produce two results. `EXPANSION_MAX_REWRITES` bounds how many
|
||||
// times the scan may restart. Real `{a},b}` input needs a handful.
|
||||
const EXPANSION_MAX_REWRITES = 1_000;
|
||||
function numeric(str) {
|
||||
return !isNaN(str) ? parseInt(str, 10) : str.charCodeAt(0);
|
||||
}
|
||||
@@ -52450,37 +52468,52 @@ function unescapeBraces(str) {
|
||||
.replace(escCommaPattern, ',')
|
||||
.replace(escPeriodPattern, '.');
|
||||
}
|
||||
// Like `target.push(...items)` but doesn't overflow the stack
|
||||
function pushAll(target, items) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
target.push(items[i]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Basically just str.split(","), but handling cases
|
||||
* where we have nested braced sections, which should be
|
||||
* treated as individual members, like {a,{b,c},d}
|
||||
*/
|
||||
function parseCommaParts(str) {
|
||||
if (!str) {
|
||||
return [''];
|
||||
}
|
||||
const parts = [];
|
||||
// Walk the brace groups iteratively. Recursing on `post` once per group let a
|
||||
// chain of them exhaust the stack - the parsing-side counterpart to
|
||||
// the `expand_` overflow fixed for CVE-2026-14257, and not something `max` or
|
||||
// `maxLength` can bound, since it happens before expansion.
|
||||
//
|
||||
// The part the next chunk continues
|
||||
let carry = '';
|
||||
for (;;) {
|
||||
const m = balanced('{', '}', str);
|
||||
if (!m) {
|
||||
return str.split(',');
|
||||
const tail = str.split(',');
|
||||
tail[0] = carry + tail[0];
|
||||
pushAll(parts, tail);
|
||||
return parts;
|
||||
}
|
||||
const { pre, body, post } = m;
|
||||
const p = pre.split(',');
|
||||
p[0] = carry + p[0];
|
||||
p[p.length - 1] += '{' + body + '}';
|
||||
const postParts = parseCommaParts(post);
|
||||
if (post.length) {
|
||||
;
|
||||
p[p.length - 1] += postParts.shift();
|
||||
p.push.apply(p, postParts);
|
||||
}
|
||||
parts.push.apply(parts, p);
|
||||
if (!post.length) {
|
||||
pushAll(parts, p);
|
||||
return parts;
|
||||
}
|
||||
carry = p.pop();
|
||||
pushAll(parts, p);
|
||||
str = post;
|
||||
}
|
||||
}
|
||||
function expand(str, options = {}) {
|
||||
if (!str) {
|
||||
return [];
|
||||
}
|
||||
const { max = EXPANSION_MAX, maxLength = EXPANSION_MAX_LENGTH } = options;
|
||||
const { max = EXPANSION_MAX, maxLength = EXPANSION_MAX_LENGTH, maxDepth = EXPANSION_MAX_DEPTH, maxRewrites = EXPANSION_MAX_REWRITES, } = options;
|
||||
// I don't know why Bash 4.3 does this, but it does.
|
||||
// Anything starting with {} will have the first two bytes preserved
|
||||
// but *only* at the top level, so {},a}b will not expand to anything,
|
||||
@@ -52490,7 +52523,7 @@ function expand(str, options = {}) {
|
||||
if (str.slice(0, 2) === '{}') {
|
||||
str = '\\{\\}' + str.slice(2);
|
||||
}
|
||||
return expand_(escapeBraces(str), max, maxLength, true).map(unescapeBraces);
|
||||
return expand_(escapeBraces(str), max, maxLength, maxDepth, 0, maxRewrites, true).map(unescapeBraces);
|
||||
}
|
||||
function embrace(str) {
|
||||
return '{' + str + '}';
|
||||
@@ -52585,7 +52618,13 @@ function expandSequence(body, isAlphaSequence, max, maxLength) {
|
||||
}
|
||||
return N;
|
||||
}
|
||||
function expand_(str, max, maxLength, isTop) {
|
||||
function expand_(str, max, maxLength, maxDepth, depth, maxRewrites, isTop) {
|
||||
// Too deeply nested to keep following: treat the rest as literal, the same
|
||||
// way a group that cannot expand is already handled. Truncating rather than
|
||||
// throwing keeps `expand` total, matching `max` and `maxLength`.
|
||||
if (depth > maxDepth) {
|
||||
return [str];
|
||||
}
|
||||
// Consume the string's top-level brace groups left to right, threading a
|
||||
// running set of combined prefixes (`acc`). Expanding the tail iteratively -
|
||||
// rather than recursing on `m.post` once per group - keeps the native stack
|
||||
@@ -52597,6 +52636,9 @@ function expand_(str, max, maxLength, isTop) {
|
||||
// comma set - a sequence like `{a..\}` may legitimately yield ''. The drop
|
||||
// is on the final strings, so it is applied to whichever `combine` produces
|
||||
// them (the one with no brace set left in the tail).
|
||||
// How many times the `{a},b}` rewrite below has restarted the scan. Each pass
|
||||
// re-reads the whole string, so leaving this unbounded is quadratic.
|
||||
let rewrites = 0;
|
||||
let dropEmpties = false;
|
||||
let firstGroup = true;
|
||||
for (;;) {
|
||||
@@ -52621,7 +52663,8 @@ function expand_(str, max, maxLength, isTop) {
|
||||
const isOptions = m.body.indexOf(',') >= 0;
|
||||
if (!isSequence && !isOptions) {
|
||||
// {a},b}
|
||||
if (m.post.match(/,(?!,).*\}/)) {
|
||||
if (rewrites < maxRewrites && m.post.match(/,(?!,).*\}/)) {
|
||||
rewrites++;
|
||||
str = m.pre + '{' + m.body + escClose + m.post;
|
||||
isTop = true;
|
||||
continue;
|
||||
@@ -52641,7 +52684,7 @@ function expand_(str, max, maxLength, isTop) {
|
||||
let n = parseCommaParts(m.body);
|
||||
if (n.length === 1 && n[0] !== undefined) {
|
||||
// x{{a,b}}y ==> x{a}y x{b}y
|
||||
n = expand_(n[0], max, maxLength, false).map(embrace);
|
||||
n = expand_(n[0], max, maxLength, maxDepth, depth + 1, maxRewrites, false).map(embrace);
|
||||
//XXX is this necessary? Can't seem to hit it in tests.
|
||||
/* c8 ignore start */
|
||||
if (n.length === 1) {
|
||||
@@ -52667,12 +52710,13 @@ function expand_(str, max, maxLength, isTop) {
|
||||
values = [];
|
||||
let valuesLength = 0;
|
||||
outer: for (let j = 0; j < n.length; j++) {
|
||||
const expanded = expand_(n[j], max, maxLength, false);
|
||||
const expanded = expand_(n[j], max, maxLength, maxDepth, depth + 1, maxRewrites, false);
|
||||
for (let k = 0; k < expanded.length; k++) {
|
||||
const v = expanded[k];
|
||||
if (dropsEmpties && !v)
|
||||
continue;
|
||||
if (values.length >= max || valuesLength + v.length > maxLength) {
|
||||
if (values.length >= max ||
|
||||
valuesLength + v.length > maxLength) {
|
||||
break outer;
|
||||
}
|
||||
values.push(v);
|
||||
|
||||
Vendored
+32
-20
@@ -13,19 +13,26 @@ export const modules = {
|
||||
/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__);
|
||||
/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(9896);
|
||||
/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_2__);
|
||||
/* harmony import */ var semver__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(2088);
|
||||
/* harmony import */ var semver__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(semver__WEBPACK_IMPORTED_MODULE_3__);
|
||||
/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(6242);
|
||||
/* harmony import */ var _platform_types_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(7444);
|
||||
/* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(4527);
|
||||
/* harmony import */ var _base_installer_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6242);
|
||||
/* harmony import */ var _platform_types_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(7444);
|
||||
/* harmony import */ var _util_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(4527);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class ZuluDistribution extends _base_installer_js__WEBPACK_IMPORTED_MODULE_4__/* .JavaBase */ .O {
|
||||
function compareNumberArrays(a, b) {
|
||||
const length = Math.max(a.length, b.length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
const diff = (a[i] ?? 0) - (b[i] ?? 0);
|
||||
if (diff !== 0) {
|
||||
return diff;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
class ZuluDistribution extends _base_installer_js__WEBPACK_IMPORTED_MODULE_3__/* .JavaBase */ .O {
|
||||
constructor(installerOptions) {
|
||||
super('Zulu', installerOptions);
|
||||
}
|
||||
@@ -39,19 +46,24 @@ class ZuluDistribution extends _base_installer_js__WEBPACK_IMPORTED_MODULE_4__/*
|
||||
? [...item.java_version, item.openjdk_build_number]
|
||||
: item.java_version;
|
||||
return {
|
||||
version: (0,_util_js__WEBPACK_IMPORTED_MODULE_6__/* .convertVersionToSemver */ .ZY)(javaVersion),
|
||||
version: (0,_util_js__WEBPACK_IMPORTED_MODULE_5__/* .convertVersionToSemver */ .ZY)(javaVersion),
|
||||
url: item.download_url,
|
||||
zuluVersion: (0,_util_js__WEBPACK_IMPORTED_MODULE_6__/* .convertVersionToSemver */ .ZY)(item.distro_version),
|
||||
javaVersion: item.java_version,
|
||||
buildNumber: item.openjdk_build_number ?? 0,
|
||||
distroVersion: item.distro_version,
|
||||
packageUuid: item.package_uuid
|
||||
};
|
||||
});
|
||||
const satisfiedVersions = availableVersions
|
||||
.filter(item => (0,_util_js__WEBPACK_IMPORTED_MODULE_6__/* .isVersionSatisfies */ .y)(version, item.version))
|
||||
.filter(item => (0,_util_js__WEBPACK_IMPORTED_MODULE_5__/* .isVersionSatisfies */ .y)(version, item.version))
|
||||
.sort((a, b) => {
|
||||
// Azul provides two versions: java_version and distro_version
|
||||
// we should sort by both fields by descending
|
||||
return (-semver__WEBPACK_IMPORTED_MODULE_3___default().compareBuild(a.version, b.version) ||
|
||||
-semver__WEBPACK_IMPORTED_MODULE_3___default().compareBuild(a.zuluVersion, b.zuluVersion));
|
||||
// Compare numerically rather than via semver build metadata: Azul
|
||||
// hotfix releases carry a 4th java_version segment (e.g. 25.0.4.1+1,
|
||||
// rendered as '25.0.4+1.1') that must rank above 25.0.4+7, whereas
|
||||
// semver.compareBuild would order the build identifiers '7' > '1'.
|
||||
return (-compareNumberArrays(a.javaVersion, b.javaVersion) ||
|
||||
b.buildNumber - a.buildNumber ||
|
||||
-compareNumberArrays(a.distroVersion, b.distroVersion));
|
||||
})
|
||||
.map((item) => ({
|
||||
version: item.version,
|
||||
@@ -85,21 +97,21 @@ class ZuluDistribution extends _base_installer_js__WEBPACK_IMPORTED_MODULE_4__/*
|
||||
_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .info */ .pq(`Downloading Java ${javaRelease.version} (${this.distribution}) from ${javaRelease.url} ...`);
|
||||
let javaArchivePath = await this.downloadAndVerify(javaRelease);
|
||||
_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .info */ .pq(`Extracting Java archive...`);
|
||||
const extension = (0,_util_js__WEBPACK_IMPORTED_MODULE_6__/* .getDownloadArchiveExtension */ .ag)();
|
||||
const extension = (0,_util_js__WEBPACK_IMPORTED_MODULE_5__/* .getDownloadArchiveExtension */ .ag)();
|
||||
if (process.platform === 'win32') {
|
||||
javaArchivePath = (0,_util_js__WEBPACK_IMPORTED_MODULE_6__/* .renameWinArchive */ .n2)(javaArchivePath);
|
||||
javaArchivePath = (0,_util_js__WEBPACK_IMPORTED_MODULE_5__/* .renameWinArchive */ .n2)(javaArchivePath);
|
||||
}
|
||||
const extractedJavaPath = await (0,_util_js__WEBPACK_IMPORTED_MODULE_6__/* .extractJdkFile */ .PE)(javaArchivePath, extension);
|
||||
const extractedJavaPath = await (0,_util_js__WEBPACK_IMPORTED_MODULE_5__/* .extractJdkFile */ .PE)(javaArchivePath, extension);
|
||||
const archiveName = fs__WEBPACK_IMPORTED_MODULE_2___default().readdirSync(extractedJavaPath)[0];
|
||||
const archivePath = path__WEBPACK_IMPORTED_MODULE_1___default().join(extractedJavaPath, archiveName);
|
||||
const javaPath = await (0,_util_js__WEBPACK_IMPORTED_MODULE_6__/* .cacheJdkDir */ .Vj)(archivePath, this.toolcacheFolderName, this.getToolcacheVersionName(javaRelease.version), this.architecture);
|
||||
const javaPath = await (0,_util_js__WEBPACK_IMPORTED_MODULE_5__/* .cacheJdkDir */ .Vj)(archivePath, this.toolcacheFolderName, this.getToolcacheVersionName(javaRelease.version), this.architecture);
|
||||
return { version: javaRelease.version, path: javaPath };
|
||||
}
|
||||
async getAvailableVersions() {
|
||||
const arch = this.getArchitectureOptions();
|
||||
const [bundleType, features] = this.packageType.split('+');
|
||||
const platform = this.getPlatformOption();
|
||||
const extension = (0,_util_js__WEBPACK_IMPORTED_MODULE_6__/* .getDownloadArchiveExtension */ .ag)();
|
||||
const extension = (0,_util_js__WEBPACK_IMPORTED_MODULE_5__/* .getDownloadArchiveExtension */ .ag)();
|
||||
const javafx = features?.includes('fx') ?? false;
|
||||
const crac = features?.includes('crac') ?? false;
|
||||
const releaseStatus = this.stable ? 'ga' : 'ea';
|
||||
@@ -185,7 +197,7 @@ class ZuluDistribution extends _base_installer_js__WEBPACK_IMPORTED_MODULE_4__/*
|
||||
// The new Metadata API's "linux" value returns both glibc and musl
|
||||
// packages, so target the libc the runner actually has. A glibc JDK
|
||||
// cannot run on Alpine.
|
||||
return (0,_platform_types_js__WEBPACK_IMPORTED_MODULE_5__/* .isAlpineLinux */ .G6)() ? 'linux_musl' : 'linux_glibc';
|
||||
return (0,_platform_types_js__WEBPACK_IMPORTED_MODULE_4__/* .isAlpineLinux */ .G6)() ? 'linux_musl' : 'linux_glibc';
|
||||
default:
|
||||
return process.platform;
|
||||
}
|
||||
|
||||
Vendored
+630
-227
File diff suppressed because it is too large
Load Diff
+199
-20
@@ -28,6 +28,7 @@
|
||||
- [Testing against different platforms](#Testing-against-different-platforms)
|
||||
- [Publishing using Apache Maven](#Publishing-using-Apache-Maven)
|
||||
- [Publishing to multiple Maven servers](#publishing-to-multiple-maven-servers)
|
||||
- [Redirecting Maven repositories with mirrors](#redirecting-maven-repositories-with-mirrors)
|
||||
- [Apache Maven with a settings path](#apache-maven-with-a-settings-path)
|
||||
- [Maven transfer progress (download logs)](#Maven-transfer-progress-download-logs)
|
||||
- [Java problem matcher (compiler annotations)](#java-problem-matcher-compiler-annotations)
|
||||
@@ -83,6 +84,12 @@ steps:
|
||||
|
||||
### Liberica
|
||||
|
||||
Exact patch releases use SDKMAN's `X.Y.Z+P.B` notation, including in `.sdkmanrc`.
|
||||
For example, `java=17.0.20+1.1-librca` selects BellSoft's `17.0.20.1+1` release.
|
||||
Alternatively, `java-version: '17.0.20.1'` selects the newest build of patch 1,
|
||||
whereas `java-version: '17.0.20+1'` selects the exact unpatched build 1.
|
||||
Floating versions such as `17` prefer newer patches before comparing build numbers.
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
@@ -151,7 +158,16 @@ steps:
|
||||
```
|
||||
|
||||
### Amazon Corretto
|
||||
**NOTE:** Amazon Corretto only supports the major version specification.
|
||||
Amazon Corretto supports major versions (for example, `25`) and full release numbers
|
||||
(for example, `25.0.5.0.10`). Full release numbers select the versioned archive directly,
|
||||
including older releases no longer listed in the latest-version feed. SHA-256 checksums
|
||||
for exact releases come from Amazon's current download catalog or GitHub release notes.
|
||||
Historical releases without a published SHA-256 cannot be downloaded.
|
||||
|
||||
Full Corretto release numbers supplied through `java-version-file` now select that exact
|
||||
release; previously they selected the latest matching major. Update MD5-only file pins
|
||||
to a release with a published SHA-256. Partial version-file values keep their existing
|
||||
major-version behavior.
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
@@ -159,7 +175,7 @@ steps:
|
||||
- uses: actions/setup-java@v6
|
||||
with:
|
||||
distribution: 'corretto'
|
||||
java-version: '25'
|
||||
java-version: '25.0.5.0.10'
|
||||
- run: java --version
|
||||
```
|
||||
|
||||
@@ -330,7 +346,7 @@ The package types have these meanings:
|
||||
| `liberica-nik` | `jdk`, `jdk+fx` | `java-version` selects the embedded JDK version, not the NIK/GraalVM release number. BellSoft currently publishes matching standard and JavaFX "full" bundles for JDK 11 and later, with gaps between feature releases. Any other `java-package` value is rejected. |
|
||||
| `microsoft` | `jdk` | Stable builds only. The bundled manifest contains Java 11, 16, 17, 21, and 25 releases; platform availability varies by release. |
|
||||
| `semeru` | `jdk`, `jre` | Stable OpenJ9 builds only. IBM publishes both image types for the supported release lines (currently 8, 11, 17, 21, and 25), subject to platform availability. |
|
||||
| `corretto` | `jdk`, `jre` | Accepts major versions only. JDK availability follows Amazon's platform catalog. For the operating systems directly selected by `setup-java`, JRE downloads are limited to Java 8 on Windows; Linux and macOS use `jdk`. |
|
||||
| `corretto` | `jdk`, `jre` | Accepts major versions and full Corretto release numbers. JDK availability follows Amazon's platform catalog. For the operating systems directly selected by `setup-java`, JRE downloads are limited to Java 8 on Windows; Linux and macOS use `jdk`. |
|
||||
| `oracle` | `jdk` | Stable Oracle JDK 17 and later only. |
|
||||
| `oracle-openjdk` | `jdk` | Installs the GA or early-access JDK builds currently listed or archived on `jdk.java.net`; use a `-ea` version such as `27-ea` for early access. |
|
||||
| `redhat` | `jdk`, `jre` | Stable builds only. Version availability follows the Foojay Disco catalog for Red Hat Build of OpenJDK and can lag Red Hat's downloads page. |
|
||||
@@ -643,13 +659,14 @@ runner, distribution, JDK size, network, and cache eviction pressure.
|
||||
|
||||
The `architecture` input is normalized before setup-java checks the tool cache
|
||||
or contacts a vendor. `amd64`, `ia32`, `arm`, and `arm64` are accepted aliases
|
||||
for `x64`, `x86`, `armv7`, and `aarch64`. The table lists the combinations
|
||||
setup-java validates up front; an individual Java patch release can still be
|
||||
absent from a vendor catalog.
|
||||
for `x64`, `x86`, `armv7`, and `aarch64`. Every value must be one token that
|
||||
starts with a letter or digit and contains only letters, digits, `_`, or `-`.
|
||||
The table lists the combinations setup-java validates up front; an individual
|
||||
Java patch release can still be absent from a vendor catalog.
|
||||
|
||||
| Distribution | Linux | macOS | Windows | Other / version restrictions |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `temurin` | `x64`, `x86`, `armv7`, `aarch64`, `ppc64le`, `s390x` | `x64`, `aarch64` | `x64`, `x86`, `aarch64` | Linux `armv7` is available through Java 17. |
|
||||
| `temurin` | `x64`, `x86`, `armv7`, `aarch64`, `ppc64le`, `riscv64`, `s390x` | `x64`, `aarch64` | `x64`, `x86`, `aarch64` | Linux `armv7` is available through Java 17. |
|
||||
| `zulu` | `x64`, `x86`, `armv7`, `aarch64` | `x64`, `aarch64` | `x64`, `x86`, `aarch64` | |
|
||||
| `liberica` | `x64`, `x86`, `armv7`, `aarch64`, `ppc64le` | `x64`, `aarch64` | `x64`, `x86`, `aarch64` | Solaris: `x64`. |
|
||||
| `liberica-nik` | `x64`, `aarch64` | `x64`, `aarch64` | `x64`, `aarch64` | |
|
||||
@@ -885,6 +902,11 @@ Use `mvn-server-credentials` to add more than one credential entry to the genera
|
||||
|
||||
When this input is set, it replaces the single server configured by `server-id`, `server-username-env-var`, and `server-password-env-var`.
|
||||
|
||||
Use `mvn-server-repository-origins` when a Maven server credential must be allowed for an explicit repository origin. Each line has the format `server-id:repository-origin`; a server can have multiple origins.
|
||||
|
||||
> [!NOTE]
|
||||
> `mvn-server-repository-origins` only works with Maven 3.10 and later. Earlier Maven versions ignore the generated `repositoryOrigins` element and log an `Unrecognised tag: 'repositoryOrigins'` warning. GitHub-hosted runners currently ship Maven 3.9.x, so use the [Maven Wrapper](https://maven.apache.org/tools/wrapper/) or install Maven 3.10+ yourself to use this feature.
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
@@ -896,6 +918,9 @@ steps:
|
||||
mvn-server-credentials: |
|
||||
releases:RELEASES_USERNAME:RELEASES_PASSWORD
|
||||
snapshots:SNAPSHOTS_USERNAME:SNAPSHOTS_PASSWORD
|
||||
mvn-server-repository-origins: |
|
||||
releases:https://central.sonatype.com
|
||||
snapshots:https://central.sonatype.com
|
||||
- name: Publish with Maven
|
||||
run: mvn deploy
|
||||
env:
|
||||
@@ -913,11 +938,17 @@ This configuration produces the following server entries:
|
||||
<id>releases</id>
|
||||
<username>${env.RELEASES_USERNAME}</username>
|
||||
<password>${env.RELEASES_PASSWORD}</password>
|
||||
<repositoryOrigins>
|
||||
<repositoryOrigin>https://central.sonatype.com</repositoryOrigin>
|
||||
</repositoryOrigins>
|
||||
</server>
|
||||
<server>
|
||||
<id>snapshots</id>
|
||||
<username>${env.SNAPSHOTS_USERNAME}</username>
|
||||
<password>${env.SNAPSHOTS_PASSWORD}</password>
|
||||
<repositoryOrigins>
|
||||
<repositoryOrigin>https://central.sonatype.com</repositoryOrigin>
|
||||
</repositoryOrigins>
|
||||
</server>
|
||||
</servers>
|
||||
```
|
||||
@@ -989,14 +1020,162 @@ This configuration adds the following active profile to `settings.xml`:
|
||||
</activeProfiles>
|
||||
```
|
||||
|
||||
Maven Central is included first by default. Set
|
||||
`mvn-repositories-prioritize-central: false` to place custom repositories
|
||||
first, or set `mvn-repositories-include-central: false` to disable Central. The
|
||||
generated profile overrides the Central repository inherited from Maven's Super
|
||||
POM with releases and snapshots disabled. When automatic Central inclusion is
|
||||
off, the ID `central` may instead be declared explicitly in `mvn-repositories`
|
||||
to replace it with a user-specified repository; otherwise that ID is reserved
|
||||
to prevent duplicate entries.
|
||||
Maven Central is included first by default. The Central controls apply to both
|
||||
dependency and plugin repositories. Set
|
||||
`mvn-repositories-prioritize-central: false` to place custom repositories first
|
||||
within each configured list, or set `mvn-repositories-include-central: false` to
|
||||
disable Central for both types. The generated profile overrides the dependency
|
||||
and plugin Central repositories inherited from Maven's Super POM with releases
|
||||
and snapshots disabled, even if only one custom repository list is supplied.
|
||||
These controls have no effect when neither list contains a nonblank entry.
|
||||
|
||||
When automatic Central inclusion is off, the ID `central` may instead be
|
||||
declared explicitly in `mvn-repositories` or `mvn-plugin-repositories` to replace
|
||||
it with a user-specified repository for that type. Declaring it in one list
|
||||
does not enable Central in the other. Otherwise that ID is reserved to prevent
|
||||
duplicate entries. Other repositories declared in your project's POM are not
|
||||
disabled by these controls.
|
||||
|
||||
### Resolving Maven plugins from custom repositories
|
||||
|
||||
Maven resolves build plugins through `<pluginRepositories>`, separately from
|
||||
dependency `<repositories>`. Use `mvn-plugin-repositories` to configure plugin
|
||||
resolution with the same multiline `repository-id:repository-url:snapshots-enabled`
|
||||
format. It can be supplied without `mvn-repositories`; dependency repositories
|
||||
are not automatically used as plugin repositories.
|
||||
|
||||
For example, configure the same private repository for dependencies and plugins
|
||||
and disable inherited Maven Central for both:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-java@v6
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '21'
|
||||
mvn-server-credentials: |
|
||||
private:PRIVATE_REPOSITORY_USERNAME:PRIVATE_REPOSITORY_TOKEN
|
||||
mvn-repositories: |
|
||||
private:https://maven.example.com/releases:false
|
||||
mvn-plugin-repositories: |
|
||||
private:https://maven.example.com/releases:false
|
||||
mvn-repositories-include-central: false
|
||||
- run: mvn --batch-mode verify
|
||||
env:
|
||||
PRIVATE_REPOSITORY_USERNAME: ${{ secrets.PRIVATE_REPOSITORY_USERNAME }}
|
||||
PRIVATE_REPOSITORY_TOKEN: ${{ secrets.PRIVATE_REPOSITORY_TOKEN }}
|
||||
```
|
||||
|
||||
The matching ID `private` selects the same server credentials for both types.
|
||||
IDs must be unique within each list, but can be shared across the two lists.
|
||||
Public repositories do not require a matching credential entry.
|
||||
|
||||
Alongside dependency repositories, the active `setup-java-repositories` profile
|
||||
contains:
|
||||
|
||||
```xml
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>private</id>
|
||||
<url>https://maven.example.com/releases</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
<pluginRepository>
|
||||
<id>central</id>
|
||||
<url>https://repo.maven.apache.org/maven2</url>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
```
|
||||
|
||||
Omitting a Central override would leave the Super POM's plugin repository
|
||||
enabled. In particular, `mvn-repositories-include-central: false` now disables
|
||||
inherited plugin Central even for existing workflows that only configure
|
||||
`mvn-repositories`. To download plugins in such workflows, configure a plugin
|
||||
repository or explicitly declare `central` in `mvn-plugin-repositories`.
|
||||
|
||||
With Central included, it is placed before custom plugin repositories by
|
||||
default; `mvn-repositories-prioritize-central: false` places it after them.
|
||||
All repository generation respects `settings-path` and `overwrite-settings`;
|
||||
an existing settings file is left untouched when `overwrite-settings: false`.
|
||||
|
||||
### Redirecting Maven repositories with mirrors
|
||||
|
||||
Use `mvn-mirrors` to redirect matching dependency and plugin repositories,
|
||||
including Maven Central and repositories declared in project or parent POMs.
|
||||
Unlike repository profiles, mirrors replace the destination Maven contacts.
|
||||
The Central controls described above do not redirect arbitrary POM repositories.
|
||||
|
||||
Each nonblank line has the format `mirror-id:mirror-url:mirror-of`. IDs must be
|
||||
unique within this input. `mirror-url` must be an absolute URL that includes a
|
||||
scheme, such as `https://`. URL schemes, ports, IPv6 addresses, and colons in
|
||||
`mirrorOf` selectors are preserved. An explicit `mirror-of` field is required
|
||||
after the complete URL, including any port. Percent-encode colons in the URL
|
||||
path, query, or fragment as `%3A`. Otherwise, text after the last unencoded
|
||||
colon may be read as the `mirror-of` value. For example:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-java@v6
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '21'
|
||||
mvn-server-credentials: |
|
||||
private:PRIVATE_REPOSITORY_USERNAME:PRIVATE_REPOSITORY_TOKEN
|
||||
mvn-mirrors: |
|
||||
private:https://maven.example.com/releases:*
|
||||
- run: mvn --batch-mode verify
|
||||
env:
|
||||
PRIVATE_REPOSITORY_USERNAME: ${{ secrets.PRIVATE_REPOSITORY_USERNAME }}
|
||||
PRIVATE_REPOSITORY_TOKEN: ${{ secrets.PRIVATE_REPOSITORY_TOKEN }}
|
||||
```
|
||||
|
||||
This adds the following directly under `<settings>`, not inside a profile:
|
||||
|
||||
```xml
|
||||
<mirrors>
|
||||
<mirror>
|
||||
<id>private</id>
|
||||
<url>https://maven.example.com/releases</url>
|
||||
<mirrorOf>*</mirrorOf>
|
||||
</mirror>
|
||||
</mirrors>
|
||||
```
|
||||
|
||||
For authentication, the mirror ID must match a server ID configured through
|
||||
`mvn-server-credentials` or the single-server inputs. Public mirrors do not need
|
||||
matching credentials. Credentials remain environment-variable references in the
|
||||
generated settings; do not put credential values in `mvn-mirrors`.
|
||||
|
||||
Mirrors can be used alone, as above, or alongside `mvn-repositories` and
|
||||
`mvn-plugin-repositories`. They do not change those inputs or the Central
|
||||
controls. Omitting `mvn-mirrors`, or supplying only blank lines, adds no
|
||||
`<mirrors>` section or repository profile.
|
||||
|
||||
Maven interprets `mirrorOf`: `*` matches every repository, `central` matches
|
||||
only that repository ID, `external:*` matches external repositories, and
|
||||
`external:http:*` matches external HTTP repositories. Comma-separated patterns
|
||||
such as `external:*,!internal` exclude specific IDs. Selector text and mirror
|
||||
order are retained; Maven applies its own matching and precedence rules.
|
||||
For example, a public mirror needs no additional repository or credential input:
|
||||
|
||||
```yaml
|
||||
mvn-mirrors: |
|
||||
public:https://mirror.example.com:8443/maven:external:*,!internal
|
||||
```
|
||||
|
||||
Mirror generation respects `settings-path` and `overwrite-settings`. An existing
|
||||
settings file is left untouched when `overwrite-settings: false`; this input
|
||||
does not merge mirrors into that file.
|
||||
|
||||
### GPG
|
||||
|
||||
@@ -1131,7 +1310,7 @@ jobs:
|
||||
|
||||
## Java problem matcher (compiler annotations)
|
||||
|
||||
By default, `setup-java` registers a [problem matcher](https://github.com/actions/toolkit/blob/main/docs/problem-matchers.md) for Java after installing the JDK. It scans the log output of subsequent steps and turns Java diagnostics into GitHub [annotations](https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#setting-a-warning-message) that appear in the run summary and inline on the affected files. It matches three kinds of lines:
|
||||
Java [problem matcher](https://github.com/actions/toolkit/blob/main/docs/problem-matchers.md) registration is disabled by default. Set `problem-matcher: true` to have `setup-java` register the matcher after installing the JDK. When enabled, it scans the log output of subsequent steps and turns Java diagnostics into GitHub [annotations](https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#setting-a-warning-message) that appear in the run summary and inline on the affected files. It matches three kinds of lines:
|
||||
|
||||
- Compiler errors and warnings, e.g. `App.java:12: error: cannot find symbol` (owner `javac`).
|
||||
- Maven compiler errors and warnings, e.g. `[ERROR] /path/App.java:[12,5] cannot find symbol` (owner `maven-javac`).
|
||||
@@ -1139,9 +1318,9 @@ By default, `setup-java` registers a [problem matcher](https://github.com/action
|
||||
|
||||
GitHub Actions limits problem matcher annotations to 10 of each severity per step and 50 annotations per job. Additional diagnostics remain available in the build log. Log grouping does not change these limits because every matched diagnostic still counts as an annotation.
|
||||
|
||||
### Disabling the problem matcher
|
||||
### Enabling the problem matcher
|
||||
|
||||
Set `problem-matcher` to `false` to prevent the matcher from being registered:
|
||||
Set `problem-matcher` to `true` to enable annotations, including in workflows that previously relied on default registration:
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
@@ -1154,13 +1333,13 @@ jobs:
|
||||
with:
|
||||
distribution: '<distribution>'
|
||||
java-version: '21'
|
||||
problem-matcher: false
|
||||
problem-matcher: true
|
||||
|
||||
- name: Build with Maven
|
||||
run: mvn package --file pom.xml
|
||||
```
|
||||
|
||||
Disabling the matcher only stops annotations from being created. Compiler output remains in the log, and compilation errors still fail the build step.
|
||||
Omit `problem-matcher` or set it to `false` to prevent this action invocation from registering the matcher. Compiler output remains in the log, and compilation errors still fail the build step. This does not unregister matchers already registered by other steps or earlier action invocations.
|
||||
|
||||
## Publishing using Gradle
|
||||
```yaml
|
||||
|
||||
Generated
+1126
-947
File diff suppressed because it is too large
Load Diff
+13
-13
@@ -42,34 +42,34 @@
|
||||
"author": "GitHub",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/cache": "^6.2.0",
|
||||
"@actions/cache": "^6.3.0",
|
||||
"@actions/core": "^3.0.1",
|
||||
"@actions/exec": "^3.0.0",
|
||||
"@actions/glob": "^0.7.0",
|
||||
"@actions/http-client": "^4.0.1",
|
||||
"@actions/io": "^3.0.2",
|
||||
"@actions/tool-cache": "^4.0.0",
|
||||
"fast-xml-parser": "^5.11.0",
|
||||
"fast-xml-parser": "^5.12.0",
|
||||
"semver": "^7.8.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@jest/globals": "^30.4.1",
|
||||
"@types/node": "^26.2.0",
|
||||
"@jest/globals": "^30.5.2",
|
||||
"@types/node": "^26.6.5",
|
||||
"@types/semver": "^7.8.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.67.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.72.0",
|
||||
"@typescript-eslint/parser": "^8.65.0",
|
||||
"@vercel/ncc": "^0.45.0",
|
||||
"eslint": "^10.8.1",
|
||||
"eslint": "^10.13.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-jest": "^29.16.1",
|
||||
"eslint-plugin-n": "^18.3.0",
|
||||
"globals": "^17.11.0",
|
||||
"eslint-plugin-jest": "^29.17.0",
|
||||
"eslint-plugin-n": "^18.5.0",
|
||||
"globals": "^17.13.0",
|
||||
"husky": "^9.1.7",
|
||||
"jest": "^30.4.2",
|
||||
"lint-staged": "^17.3.0",
|
||||
"prettier": "^3.9.5",
|
||||
"ts-jest": "^29.4.11",
|
||||
"jest": "^30.5.2",
|
||||
"lint-staged": "^17.6.0",
|
||||
"prettier": "^3.9.10",
|
||||
"ts-jest": "^29.4.15",
|
||||
"typescript": "^6.0.3"
|
||||
},
|
||||
"bugs": {
|
||||
|
||||
+260
-58
@@ -13,6 +13,7 @@ export interface MavenServerCredentials {
|
||||
id: string;
|
||||
usernameEnvVar: string;
|
||||
passwordEnvVar: string;
|
||||
repositoryOrigins?: string[];
|
||||
}
|
||||
|
||||
export interface MavenRepository {
|
||||
@@ -24,13 +25,25 @@ export interface MavenRepository {
|
||||
|
||||
export interface MavenRepositorySettings {
|
||||
repositories: MavenRepository[];
|
||||
pluginRepositories?: MavenRepository[];
|
||||
includeCentral: boolean;
|
||||
prioritizeCentral: boolean;
|
||||
}
|
||||
|
||||
export interface MavenMirror {
|
||||
id: string;
|
||||
url: string;
|
||||
mirrorOf: string;
|
||||
}
|
||||
|
||||
const MAVEN_REPOSITORY_ORIGIN = /^[A-Za-z][A-Za-z0-9+.-]*:\/\/[^/?#@]+\/?$/;
|
||||
|
||||
export async function configureAuthentication() {
|
||||
const servers = getMavenServerSettings();
|
||||
const repositorySettings = getMavenRepositorySettings();
|
||||
const mirrors = parseMavenMirrors(
|
||||
core.getMultilineInput(constants.INPUT_MVN_MIRRORS)
|
||||
);
|
||||
const settingsDirectory =
|
||||
core.getInput(constants.INPUT_SETTINGS_PATH) ||
|
||||
path.join(os.homedir(), constants.M2_DIR);
|
||||
@@ -56,7 +69,8 @@ export async function configureAuthentication() {
|
||||
settingsDirectory,
|
||||
overwriteSettings,
|
||||
gpgPassphraseEnvVar,
|
||||
repositorySettings
|
||||
repositorySettings,
|
||||
mirrors
|
||||
);
|
||||
|
||||
if (gpgPrivateKey) {
|
||||
@@ -95,11 +109,9 @@ export function getMavenServerSettings(): MavenServerCredentials[] {
|
||||
constants.INPUT_MVN_SERVER_CREDENTIALS
|
||||
);
|
||||
|
||||
if (entries.some(entry => entry.trim())) {
|
||||
return parseMavenServerCredentials(entries);
|
||||
}
|
||||
|
||||
return [
|
||||
const servers = entries.some(entry => entry.trim())
|
||||
? parseMavenServerCredentials(entries)
|
||||
: [
|
||||
{
|
||||
id: core.getInput(constants.INPUT_SERVER_ID),
|
||||
usernameEnvVar: getInputWithDeprecatedAlias(
|
||||
@@ -114,6 +126,11 @@ export function getMavenServerSettings(): MavenServerCredentials[] {
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
return addMavenServerRepositoryOrigins(
|
||||
servers,
|
||||
core.getMultilineInput(constants.INPUT_MVN_SERVER_REPOSITORY_ORIGINS)
|
||||
);
|
||||
}
|
||||
|
||||
// only exported for testing purposes
|
||||
@@ -156,11 +173,96 @@ export function parseMavenServerCredentials(
|
||||
return servers;
|
||||
}
|
||||
|
||||
// only exported for testing purposes
|
||||
export function addMavenServerRepositoryOrigins(
|
||||
servers: MavenServerCredentials[],
|
||||
entries: string[]
|
||||
): MavenServerCredentials[] {
|
||||
const serverIds = new Set(servers.map(server => server.id));
|
||||
const originsByServer = new Map<string, string[]>();
|
||||
|
||||
entries.forEach((entry, index) => {
|
||||
if (!entry.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const separator = entry.indexOf(':');
|
||||
if (separator <= 0 || separator === entry.length - 1) {
|
||||
throw new Error(
|
||||
`Invalid mvn-server-repository-origins entry at line ${index + 1}. Expected format: server-id:repository-origin`
|
||||
);
|
||||
}
|
||||
|
||||
const id = entry.slice(0, separator).trim();
|
||||
const value = entry.slice(separator + 1).trim();
|
||||
if (!id || !value) {
|
||||
throw new Error(
|
||||
`Invalid mvn-server-repository-origins entry at line ${index + 1}. Server ID and repository origin are required`
|
||||
);
|
||||
}
|
||||
if (!serverIds.has(id)) {
|
||||
throw new Error(
|
||||
`Unknown server-id '${id}' in mvn-server-repository-origins at line ${index + 1}`
|
||||
);
|
||||
}
|
||||
|
||||
let url: URL;
|
||||
try {
|
||||
if (!MAVEN_REPOSITORY_ORIGIN.test(value)) {
|
||||
throw new Error();
|
||||
}
|
||||
url = new URL(value);
|
||||
} catch {
|
||||
throw new Error(
|
||||
`Invalid repository origin '${value}' in mvn-server-repository-origins at line ${index + 1}`
|
||||
);
|
||||
}
|
||||
if (
|
||||
!url.host ||
|
||||
url.username ||
|
||||
url.password ||
|
||||
(url.pathname !== '' && url.pathname !== '/') ||
|
||||
url.search ||
|
||||
url.hash
|
||||
) {
|
||||
throw new Error(
|
||||
`Invalid repository origin '${value}' in mvn-server-repository-origins at line ${index + 1}`
|
||||
);
|
||||
}
|
||||
|
||||
const explicitPort = /:(\d+)\/?$/.exec(value)?.[1];
|
||||
const port = explicitPort ? Number.parseInt(explicitPort, 10) : undefined;
|
||||
const includePort =
|
||||
port !== undefined &&
|
||||
!(
|
||||
(url.protocol === 'http:' && port === 80) ||
|
||||
(url.protocol === 'https:' && port === 443)
|
||||
);
|
||||
const origin = `${url.protocol}//${url.hostname}${includePort ? `:${port}` : ''}`;
|
||||
const origins = originsByServer.get(id) || [];
|
||||
if (!origins.includes(origin)) {
|
||||
origins.push(origin);
|
||||
originsByServer.set(id, origins);
|
||||
}
|
||||
});
|
||||
|
||||
return servers.map(server => {
|
||||
const repositoryOrigins = originsByServer.get(server.id);
|
||||
return repositoryOrigins ? {...server, repositoryOrigins} : server;
|
||||
});
|
||||
}
|
||||
|
||||
// only exported for testing purposes
|
||||
export function getMavenRepositorySettings():
|
||||
MavenRepositorySettings | undefined {
|
||||
const entries = core.getMultilineInput(constants.INPUT_MVN_REPOSITORIES);
|
||||
if (!entries.some(entry => entry.trim())) {
|
||||
const pluginEntries = core.getMultilineInput(
|
||||
constants.INPUT_MVN_PLUGIN_REPOSITORIES
|
||||
);
|
||||
if (
|
||||
!entries.some(entry => entry.trim()) &&
|
||||
!pluginEntries.some(entry => entry.trim())
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -170,6 +272,11 @@ export function getMavenRepositorySettings():
|
||||
);
|
||||
return {
|
||||
repositories: parseMavenRepositories(entries, includeCentral),
|
||||
pluginRepositories: parseMavenRepositories(
|
||||
pluginEntries,
|
||||
includeCentral,
|
||||
constants.INPUT_MVN_PLUGIN_REPOSITORIES
|
||||
),
|
||||
includeCentral,
|
||||
prioritizeCentral: getBooleanInput(
|
||||
constants.INPUT_MVN_REPOSITORIES_PRIORITIZE_CENTRAL,
|
||||
@@ -181,7 +288,8 @@ export function getMavenRepositorySettings():
|
||||
// only exported for testing purposes
|
||||
export function parseMavenRepositories(
|
||||
entries: string[],
|
||||
includeCentral: boolean
|
||||
includeCentral: boolean,
|
||||
inputName = constants.INPUT_MVN_REPOSITORIES
|
||||
): MavenRepository[] {
|
||||
const repositories: MavenRepository[] = [];
|
||||
const repositoryIds = new Set<string>();
|
||||
@@ -195,7 +303,7 @@ export function parseMavenRepositories(
|
||||
const lastSeparator = entry.lastIndexOf(':');
|
||||
if (firstSeparator <= 0 || lastSeparator <= firstSeparator) {
|
||||
throw new Error(
|
||||
`Invalid mvn-repositories entry at line ${index + 1}. Expected format: repository-id:repository-url:snapshots-enabled`
|
||||
`Invalid ${inputName} entry at line ${index + 1}. Expected format: repository-id:repository-url:snapshots-enabled`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -207,18 +315,16 @@ export function parseMavenRepositories(
|
||||
.toLowerCase();
|
||||
if (!id || !url || !snapshotsValue) {
|
||||
throw new Error(
|
||||
`Invalid mvn-repositories entry at line ${index + 1}. repository-id, repository URL, and snapshots-enabled are required`
|
||||
`Invalid ${inputName} entry at line ${index + 1}. repository-id, repository URL, and snapshots-enabled are required`
|
||||
);
|
||||
}
|
||||
if (snapshotsValue !== 'true' && snapshotsValue !== 'false') {
|
||||
throw new Error(
|
||||
`Invalid snapshots-enabled value '${snapshotsValue}' in mvn-repositories entry at line ${index + 1}. Expected true or false`
|
||||
`Invalid snapshots-enabled value '${snapshotsValue}' in ${inputName} entry at line ${index + 1}. Expected true or false`
|
||||
);
|
||||
}
|
||||
if (repositoryIds.has(id)) {
|
||||
throw new Error(
|
||||
`Duplicate repository-id '${id}' in mvn-repositories input`
|
||||
);
|
||||
throw new Error(`Duplicate repository-id '${id}' in ${inputName} input`);
|
||||
}
|
||||
if (includeCentral && id === constants.MAVEN_CENTRAL_REPOSITORY_ID) {
|
||||
throw new Error(
|
||||
@@ -237,12 +343,112 @@ export function parseMavenRepositories(
|
||||
return repositories;
|
||||
}
|
||||
|
||||
// only exported for testing purposes
|
||||
export function parseMavenMirrors(entries: string[]): MavenMirror[] {
|
||||
const mirrors: MavenMirror[] = [];
|
||||
const mirrorIds = new Set<string>();
|
||||
|
||||
entries.forEach((entry, index) => {
|
||||
if (!entry.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const separator = entry.indexOf(':');
|
||||
const remainder = entry.slice(separator + 1).trim();
|
||||
// Protect scheme/authority colons (including IPv6 and ports) before
|
||||
// finding the selector, which may itself contain external:http:*.
|
||||
const urlPrefix =
|
||||
/^[A-Za-z][A-Za-z0-9+.-]*:(?:\/\/(?:[^/?#@\s]*@)?(?:\[[^\]]+\]|[^:/?#\s]+)(?::\d+(?=[/?#:\s]|$))?)?/.exec(
|
||||
remainder
|
||||
)?.[0] ?? '';
|
||||
const fields =
|
||||
/^(.*?):((?:\s*!?external:(?:http:)?\*\s*|[^:,]*)(?:,(?:\s*!?external:(?:http:)?\*\s*|[^:,]*))*)$/.exec(
|
||||
remainder.slice(urlPrefix.length)
|
||||
);
|
||||
if (separator < 0 || !fields) {
|
||||
throw new Error(
|
||||
`Invalid mvn-mirrors entry at line ${index + 1}. Expected format: mirror-id:mirror-url:mirror-of`
|
||||
);
|
||||
}
|
||||
|
||||
const id = entry.slice(0, separator).trim();
|
||||
const url = `${urlPrefix}${fields[1]}`.trim();
|
||||
const mirrorOf = fields[2].trim();
|
||||
if (!id || !url || !mirrorOf) {
|
||||
throw new Error(
|
||||
`Invalid mvn-mirrors entry at line ${index + 1}. mirror-id, mirror URL, and mirror-of are required`
|
||||
);
|
||||
}
|
||||
if (mirrorIds.has(id)) {
|
||||
throw new Error(
|
||||
`Duplicate mirror-id '${id}' in mvn-mirrors input at line ${index + 1}`
|
||||
);
|
||||
}
|
||||
|
||||
mirrorIds.add(id);
|
||||
mirrors.push({id, url, mirrorOf});
|
||||
});
|
||||
|
||||
return mirrors;
|
||||
}
|
||||
|
||||
function generateRepositorySection(
|
||||
customRepositories: MavenRepository[],
|
||||
settings: MavenRepositorySettings,
|
||||
element: 'repository' | 'pluginRepository'
|
||||
): string[] {
|
||||
if (!customRepositories.length && settings.includeCentral) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const centralRepository: MavenRepository = {
|
||||
id: constants.MAVEN_CENTRAL_REPOSITORY_ID,
|
||||
url: constants.MAVEN_CENTRAL_REPOSITORY_URL,
|
||||
snapshotsEnabled: false
|
||||
};
|
||||
const customCentralConfigured = customRepositories.some(
|
||||
repository => repository.id === constants.MAVEN_CENTRAL_REPOSITORY_ID
|
||||
);
|
||||
const repositories = settings.includeCentral
|
||||
? settings.prioritizeCentral
|
||||
? [centralRepository, ...customRepositories]
|
||||
: [...customRepositories, centralRepository]
|
||||
: customCentralConfigured
|
||||
? customRepositories
|
||||
: [...customRepositories, {...centralRepository, releasesEnabled: false}];
|
||||
const container =
|
||||
element === 'repository' ? 'repositories' : 'pluginRepositories';
|
||||
const lines = [` <${container}>`];
|
||||
|
||||
for (const repository of repositories) {
|
||||
lines.push(
|
||||
` <${element}>`,
|
||||
` <id>${escapeXmlText(repository.id)}</id>`,
|
||||
` <url>${escapeXmlText(repository.url)}</url>`,
|
||||
...(repository.releasesEnabled === undefined
|
||||
? []
|
||||
: [
|
||||
' <releases>',
|
||||
` <enabled>${repository.releasesEnabled}</enabled>`,
|
||||
' </releases>'
|
||||
]),
|
||||
' <snapshots>',
|
||||
` <enabled>${repository.snapshotsEnabled}</enabled>`,
|
||||
' </snapshots>',
|
||||
` </${element}>`
|
||||
);
|
||||
}
|
||||
lines.push(` </${container}>`);
|
||||
return lines;
|
||||
}
|
||||
|
||||
export async function createAuthenticationSettings(
|
||||
servers: MavenServerCredentials[],
|
||||
settingsDirectory: string,
|
||||
overwriteSettings: boolean,
|
||||
gpgPassphraseEnvVar: string | undefined = undefined,
|
||||
repositorySettings: MavenRepositorySettings | undefined = undefined
|
||||
repositorySettings: MavenRepositorySettings | undefined = undefined,
|
||||
mirrors: MavenMirror[] = []
|
||||
) {
|
||||
core.info(
|
||||
`Creating ${constants.MVN_SETTINGS_FILE} with server-id: ${servers.map(server => server.id).join(', ')}`
|
||||
@@ -252,7 +458,7 @@ export async function createAuthenticationSettings(
|
||||
await io.mkdirP(settingsDirectory);
|
||||
await write(
|
||||
settingsDirectory,
|
||||
generate(servers, gpgPassphraseEnvVar, repositorySettings),
|
||||
generate(servers, gpgPassphraseEnvVar, repositorySettings, mirrors),
|
||||
overwriteSettings
|
||||
);
|
||||
}
|
||||
@@ -261,7 +467,8 @@ export async function createAuthenticationSettings(
|
||||
export function generate(
|
||||
servers: MavenServerCredentials[],
|
||||
gpgPassphraseEnvVar?: string | undefined,
|
||||
repositorySettings?: MavenRepositorySettings | undefined
|
||||
repositorySettings?: MavenRepositorySettings | undefined,
|
||||
mirrors: MavenMirror[] = []
|
||||
) {
|
||||
// The maven-gpg-plugin reads the passphrase from the environment variable
|
||||
// named by the `gpg.passphraseEnvName` property (default MAVEN_GPG_PASSPHRASE).
|
||||
@@ -286,58 +493,53 @@ export function generate(
|
||||
' <server>',
|
||||
` <id>${escapeXmlText(server.id)}</id>`,
|
||||
` <username>${escapeXmlText(`\${env.${server.usernameEnvVar}}`)}</username>`,
|
||||
` <password>${escapeXmlText(`\${env.${server.passwordEnvVar}}`)}</password>`,
|
||||
' </server>'
|
||||
` <password>${escapeXmlText(`\${env.${server.passwordEnvVar}}`)}</password>`
|
||||
);
|
||||
if (server.repositoryOrigins) {
|
||||
lines.push(' <repositoryOrigins>');
|
||||
for (const origin of server.repositoryOrigins) {
|
||||
lines.push(
|
||||
` <repositoryOrigin>${escapeXmlText(origin)}</repositoryOrigin>`
|
||||
);
|
||||
}
|
||||
lines.push(' </repositoryOrigins>');
|
||||
}
|
||||
lines.push(' </server>');
|
||||
}
|
||||
lines.push(' </servers>');
|
||||
|
||||
if (mirrors.length) {
|
||||
lines.push(' <mirrors>');
|
||||
for (const mirror of mirrors) {
|
||||
lines.push(
|
||||
' <mirror>',
|
||||
` <id>${escapeXmlText(mirror.id)}</id>`,
|
||||
` <url>${escapeXmlText(mirror.url)}</url>`,
|
||||
` <mirrorOf>${escapeXmlText(mirror.mirrorOf)}</mirrorOf>`,
|
||||
' </mirror>'
|
||||
);
|
||||
}
|
||||
lines.push(' </mirrors>');
|
||||
}
|
||||
|
||||
if (repositorySettings || includeGpgPassphraseProfile) {
|
||||
lines.push(' <profiles>');
|
||||
if (repositorySettings) {
|
||||
const centralRepository: MavenRepository = {
|
||||
id: constants.MAVEN_CENTRAL_REPOSITORY_ID,
|
||||
url: constants.MAVEN_CENTRAL_REPOSITORY_URL,
|
||||
snapshotsEnabled: false
|
||||
};
|
||||
const customCentralConfigured = repositorySettings.repositories.some(
|
||||
repository => repository.id === constants.MAVEN_CENTRAL_REPOSITORY_ID
|
||||
);
|
||||
const repositories = repositorySettings.includeCentral
|
||||
? repositorySettings.prioritizeCentral
|
||||
? [centralRepository, ...repositorySettings.repositories]
|
||||
: [...repositorySettings.repositories, centralRepository]
|
||||
: customCentralConfigured
|
||||
? repositorySettings.repositories
|
||||
: [
|
||||
...repositorySettings.repositories,
|
||||
{...centralRepository, releasesEnabled: false}
|
||||
];
|
||||
|
||||
lines.push(
|
||||
' <profile>',
|
||||
` <id>${constants.MAVEN_REPOSITORIES_PROFILE_ID}</id>`,
|
||||
' <repositories>'
|
||||
...generateRepositorySection(
|
||||
repositorySettings.repositories,
|
||||
repositorySettings,
|
||||
'repository'
|
||||
),
|
||||
...generateRepositorySection(
|
||||
repositorySettings.pluginRepositories ?? [],
|
||||
repositorySettings,
|
||||
'pluginRepository'
|
||||
),
|
||||
' </profile>'
|
||||
);
|
||||
for (const repository of repositories) {
|
||||
lines.push(
|
||||
' <repository>',
|
||||
` <id>${escapeXmlText(repository.id)}</id>`,
|
||||
` <url>${escapeXmlText(repository.url)}</url>`,
|
||||
...(repository.releasesEnabled === undefined
|
||||
? []
|
||||
: [
|
||||
' <releases>',
|
||||
` <enabled>${repository.releasesEnabled}</enabled>`,
|
||||
' </releases>'
|
||||
]),
|
||||
' <snapshots>',
|
||||
` <enabled>${repository.snapshotsEnabled}</enabled>`,
|
||||
' </snapshots>',
|
||||
' </repository>'
|
||||
);
|
||||
}
|
||||
lines.push(' </repositories>', ' </profile>');
|
||||
}
|
||||
if (includeGpgPassphraseProfile) {
|
||||
lines.push(
|
||||
|
||||
@@ -16,7 +16,11 @@ export const SIGNATURE_VERIFICATION_DOCUMENTATION_URL =
|
||||
'https://github.com/actions/setup-java#download-integrity-and-signatures';
|
||||
export const SIGNATURE_VERIFICATION_FAILURE_HELP = `If this is a legitimate vendor signing-key rotation, see ${SIGNATURE_VERIFICATION_DOCUMENTATION_URL} for instructions to configure the updated public key or temporarily disable signature verification.`;
|
||||
export const INPUT_MVN_SERVER_CREDENTIALS = 'mvn-server-credentials';
|
||||
export const INPUT_MVN_SERVER_REPOSITORY_ORIGINS =
|
||||
'mvn-server-repository-origins';
|
||||
export const INPUT_MVN_REPOSITORIES = 'mvn-repositories';
|
||||
export const INPUT_MVN_PLUGIN_REPOSITORIES = 'mvn-plugin-repositories';
|
||||
export const INPUT_MVN_MIRRORS = 'mvn-mirrors';
|
||||
export const INPUT_MVN_REPOSITORIES_INCLUDE_CENTRAL =
|
||||
'mvn-repositories-include-central';
|
||||
export const INPUT_MVN_REPOSITORIES_PRIORITIZE_CENTRAL =
|
||||
|
||||
@@ -5,9 +5,9 @@ import semver from 'semver';
|
||||
import path from 'path';
|
||||
import * as httpm from '@actions/http-client';
|
||||
import {
|
||||
convertVersionToSemver,
|
||||
getToolcachePath,
|
||||
isVersionSatisfies
|
||||
isVersionSatisfies,
|
||||
normalizeJavaVersionToSemver
|
||||
} from '../util.js';
|
||||
import type {
|
||||
ChecksumAlgorithm,
|
||||
@@ -241,7 +241,7 @@ export abstract class JavaBase {
|
||||
if (javaRelease.floating) {
|
||||
if (
|
||||
!semver.valid(foundJava.version) ||
|
||||
!isVersionSatisfies(this.version, foundJava.version)
|
||||
!this.isVersionSatisfied(this.version, foundJava.version)
|
||||
) {
|
||||
throw new Error(
|
||||
`The downloaded ${this.distribution} artifact reported Java ${foundJava.version}, which does not satisfy '${this.version}'.`
|
||||
@@ -401,7 +401,7 @@ export abstract class JavaBase {
|
||||
}
|
||||
if (
|
||||
!semver.valid(restored.release.version) ||
|
||||
!isVersionSatisfies(this.version, restored.release.version)
|
||||
!this.isVersionSatisfied(this.version, restored.release.version)
|
||||
) {
|
||||
core.debug(
|
||||
`Ignoring the cached concrete version '${restored.release.version}' for ${this.distribution} ${this.version}.`
|
||||
@@ -622,10 +622,10 @@ export abstract class JavaBase {
|
||||
.filter(item => item.stable === this.stable);
|
||||
|
||||
const satisfiedVersions = availableVersions
|
||||
.filter(item => isVersionSatisfies(this.version, item.version))
|
||||
.filter(item => this.isVersionSatisfied(this.version, item.version))
|
||||
.filter(item => item.path)
|
||||
.sort((a, b) => {
|
||||
return -semver.compareBuild(a.version, b.version);
|
||||
return -this.compareVersions(a.version, b.version);
|
||||
});
|
||||
if (!satisfiedVersions || satisfiedVersions.length === 0) {
|
||||
return null;
|
||||
@@ -637,6 +637,14 @@ export abstract class JavaBase {
|
||||
};
|
||||
}
|
||||
|
||||
protected isVersionSatisfied(range: string, version: string): boolean {
|
||||
return isVersionSatisfies(range, version);
|
||||
}
|
||||
|
||||
protected compareVersions(a: string, b: string): number {
|
||||
return semver.compareBuild(a, b);
|
||||
}
|
||||
|
||||
protected normalizeVersion(version: string) {
|
||||
let stable = true;
|
||||
const latest = false;
|
||||
@@ -675,12 +683,9 @@ export abstract class JavaBase {
|
||||
|
||||
// Java uses a versioning scheme (JEP 322) that can contain more numeric
|
||||
// fields than SemVer allows, e.g. '18.0.1.1' or '11.0.9.1'. Convert such
|
||||
// exact versions to SemVer build notation ('18.0.1+1') so they are
|
||||
// accepted. Ranges and versions that already carry build metadata are
|
||||
// left untouched.
|
||||
if (/^\d+(\.\d+){3,}$/.test(version)) {
|
||||
version = convertVersionToSemver(version);
|
||||
}
|
||||
// exact versions to SemVer build notation ('18.0.1+1', or '26.0.2+1.1'
|
||||
// for '26.0.2.1+1') so they are accepted. Ranges are left untouched.
|
||||
version = normalizeJavaVersionToSemver(version);
|
||||
|
||||
if (!semver.validRange(version)) {
|
||||
throw new Error(
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import * as core from '@actions/core';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import {HttpCodes} from '@actions/http-client';
|
||||
import {
|
||||
cacheJdkDir,
|
||||
extractJdkFile,
|
||||
getDownloadArchiveExtension,
|
||||
getGitHubHttpHeaders,
|
||||
isExactCorrettoVersion,
|
||||
convertVersionToSemver,
|
||||
renameWinArchive
|
||||
} from '../../util.js';
|
||||
@@ -24,8 +27,19 @@ const CORRETTO_VERSIONS_URL =
|
||||
'https://corretto.github.io/corretto-downloads/latest_links/indexmap_with_checksum.json';
|
||||
|
||||
export class CorrettoDistribution extends JavaBase {
|
||||
private readonly requestedVersion: string;
|
||||
|
||||
constructor(installerOptions: JavaInstallerOptions) {
|
||||
super('Corretto', installerOptions);
|
||||
super('Corretto', {
|
||||
...installerOptions,
|
||||
// Java 8 releases use zero-padded fields, e.g. 8.472.08.1.
|
||||
version: isExactCorrettoVersion(installerOptions.version)
|
||||
? installerOptions.version.replace(/(^|\.)0+(?=\d)/g, '$1')
|
||||
: installerOptions.version
|
||||
});
|
||||
this.requestedVersion = installerOptions.version
|
||||
.replace('+', '.')
|
||||
.replace(/^8\.(\d+)\.(\d)\./, '8.$1.0$2.');
|
||||
}
|
||||
|
||||
protected async downloadTool(
|
||||
@@ -63,6 +77,9 @@ export class CorrettoDistribution extends JavaBase {
|
||||
if (!this.stable) {
|
||||
throw new Error('Early access versions are not supported');
|
||||
}
|
||||
if (isExactCorrettoVersion(this.requestedVersion)) {
|
||||
return this.findExactRelease();
|
||||
}
|
||||
const availableVersions = await this.getAvailableVersions();
|
||||
|
||||
// The `latest` alias is normalized to the SemVer wildcard, but Corretto
|
||||
@@ -83,7 +100,9 @@ export class CorrettoDistribution extends JavaBase {
|
||||
}
|
||||
|
||||
if (version.includes('.')) {
|
||||
throw new Error('Only major versions are supported');
|
||||
throw new Error(
|
||||
'Specify a major version or a full Corretto release, such as 25.0.5.0.10'
|
||||
);
|
||||
}
|
||||
const matchingVersions = availableVersions
|
||||
.filter(item => item.version == version)
|
||||
@@ -110,6 +129,91 @@ export class CorrettoDistribution extends JavaBase {
|
||||
return resolvedVersion;
|
||||
}
|
||||
|
||||
private async findExactRelease(): Promise<JavaDownloadRelease> {
|
||||
const version = this.requestedVersion;
|
||||
const major = version.split('.')[0];
|
||||
const platform = this.getPlatformOption();
|
||||
const arch = this.architecture;
|
||||
if (platform !== 'windows' && this.packageType !== 'jdk') {
|
||||
throw new Error('Corretto JRE archives are only available for Windows');
|
||||
}
|
||||
const os =
|
||||
platform === 'macos'
|
||||
? 'macosx'
|
||||
: platform === 'alpine'
|
||||
? 'alpine-linux'
|
||||
: platform;
|
||||
const suffix =
|
||||
platform === 'windows' ? `-${this.packageType}.zip` : '.tar.gz';
|
||||
const url = `https://corretto.aws/downloads/resources/${version}/amazon-corretto-${version}-${os}-${arch}${suffix}`;
|
||||
|
||||
// Versioned archives have no checksum sidecar; Amazon publishes their SHA-256s in the release notes.
|
||||
const source = `https://api.github.com/repos/corretto/corretto-${major}/releases/tags/${version}`;
|
||||
const findCatalogRelease = async (): Promise<
|
||||
JavaDownloadRelease | undefined
|
||||
> => {
|
||||
const archive = (await this.getAvailableVersions()).find(
|
||||
item => item.downloadLink === url
|
||||
);
|
||||
if (!archive) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
version: this.version,
|
||||
url,
|
||||
checksum: {
|
||||
algorithm: 'sha256',
|
||||
value: archive.checksum_sha256,
|
||||
source: CORRETTO_VERSIONS_URL
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
let response;
|
||||
try {
|
||||
response = await this.http.getJson<{body: string | null}>(source, {
|
||||
...getGitHubHttpHeaders(),
|
||||
accept: 'application/vnd.github+json'
|
||||
});
|
||||
} catch (error) {
|
||||
const catalogRelease = await findCatalogRelease();
|
||||
if (catalogRelease) {
|
||||
return catalogRelease;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
const row = response.result?.body
|
||||
?.split(/\r?\n/)
|
||||
.find(line => line.includes(`](${url})`));
|
||||
const checksum = row?.match(/`([a-fA-F0-9]{64})`/)?.[1];
|
||||
if (checksum) {
|
||||
return {
|
||||
version: this.version,
|
||||
url,
|
||||
checksum: {algorithm: 'sha256', value: checksum, source}
|
||||
};
|
||||
}
|
||||
|
||||
// The current catalog also covers releases missing from GitHub, such as Corretto 15 and 16.
|
||||
const catalogRelease = await findCatalogRelease();
|
||||
if (catalogRelease) {
|
||||
return catalogRelease;
|
||||
}
|
||||
if (response.statusCode === HttpCodes.NotFound) {
|
||||
throw new Error(
|
||||
`Corretto release ${version} was not found in Amazon's GitHub releases or current download catalog`
|
||||
);
|
||||
}
|
||||
if (!row) {
|
||||
throw new Error(
|
||||
`No ${platform}/${arch} ${this.packageType} archive is listed for Corretto ${version}`
|
||||
);
|
||||
}
|
||||
throw new Error(
|
||||
`No SHA-256 checksum is published for Corretto ${version} (${platform}, ${arch}, ${this.packageType})`
|
||||
);
|
||||
}
|
||||
|
||||
private async getAvailableVersions(): Promise<ICorrettoAvailableVersions[]> {
|
||||
const platform = this.getPlatformOption();
|
||||
const arch = this.distributionArchitecture();
|
||||
|
||||
@@ -16,8 +16,8 @@ import {
|
||||
extractJdkFile,
|
||||
getGitHubToken,
|
||||
getNextPageUrlFromLinkHeader,
|
||||
isVersionSatisfies,
|
||||
MAX_PAGINATION_PAGES,
|
||||
normalizeJavaVersionToSemver,
|
||||
validatePaginationUrl
|
||||
} from '../../util.js';
|
||||
import {OutgoingHttpHeaders} from 'http';
|
||||
@@ -38,7 +38,9 @@ export class JetBrainsDistribution extends JavaBase {
|
||||
const versionsRaw = await this.getAvailableVersions();
|
||||
|
||||
const versions = versionsRaw.map(v => {
|
||||
const formattedVersion = `${v.semver}+${v.build}`;
|
||||
const formattedVersion = normalizeJavaVersionToSemver(
|
||||
`${v.semver}+${v.build}`
|
||||
);
|
||||
|
||||
return {
|
||||
version: formattedVersion,
|
||||
@@ -47,9 +49,9 @@ export class JetBrainsDistribution extends JavaBase {
|
||||
});
|
||||
|
||||
const satisfiedVersions = versions
|
||||
.filter(item => isVersionSatisfies(range, item.version))
|
||||
.filter(item => this.isVersionSatisfied(range, item.version))
|
||||
.sort((a, b) => {
|
||||
return -semver.compareBuild(a.version, b.version);
|
||||
return -this.compareVersions(a.version, b.version);
|
||||
});
|
||||
|
||||
const resolvedFullVersion =
|
||||
@@ -74,6 +76,61 @@ export class JetBrainsDistribution extends JavaBase {
|
||||
};
|
||||
}
|
||||
|
||||
protected normalizeVersion(version: string) {
|
||||
const normalized = super.normalizeVersion(version);
|
||||
if (/^\d+\.\d+\.\d+(?:\.\d+)+(?:-ea)?$/.test(version)) {
|
||||
// A patch-only request must match any JBR build of that patch.
|
||||
normalized.version = version.replace(/-ea$/, '');
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
protected isVersionSatisfied(range: string, version: string): boolean {
|
||||
const patchRequest = range.match(/^(\d+\.\d+\.\d+)((?:\.\d+)+)$/);
|
||||
if (patchRequest) {
|
||||
const candidate = semver.parse(version);
|
||||
return (
|
||||
candidate !== null &&
|
||||
semver.eq(patchRequest[1], candidate) &&
|
||||
(candidate.build.slice(0, -2).join('.') || '0') ===
|
||||
patchRequest[2].slice(1)
|
||||
);
|
||||
}
|
||||
return super.isVersionSatisfied(range, version);
|
||||
}
|
||||
|
||||
protected compareVersions(a: string, b: string): number {
|
||||
const mainComparison = semver.compare(a, b);
|
||||
if (mainComparison !== 0) {
|
||||
return mainComparison;
|
||||
}
|
||||
|
||||
// JBR builds have two components (one in older releases). Any preceding
|
||||
// metadata components are Java patch fields and take priority over builds.
|
||||
const first = new semver.SemVer(a).build;
|
||||
const second = new semver.SemVer(b).build;
|
||||
const firstPatch = first.slice(0, -2);
|
||||
const secondPatch = second.slice(0, -2);
|
||||
for (
|
||||
let index = 0;
|
||||
index < Math.max(firstPatch.length, secondPatch.length);
|
||||
index++
|
||||
) {
|
||||
const patchComparison = semver.compareIdentifiers(
|
||||
firstPatch[index] ?? '0',
|
||||
secondPatch[index] ?? '0'
|
||||
);
|
||||
if (patchComparison !== 0) {
|
||||
return patchComparison;
|
||||
}
|
||||
}
|
||||
|
||||
return semver.compareBuild(
|
||||
first.length ? `0.0.0+${first.slice(-2).join('.')}` : '0.0.0',
|
||||
second.length ? `0.0.0+${second.slice(-2).join('.')}` : '0.0.0'
|
||||
);
|
||||
}
|
||||
|
||||
protected async downloadTool(
|
||||
javaRelease: JavaDownloadRelease
|
||||
): Promise<JavaInstallerResults> {
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
cacheJdkDir,
|
||||
extractJdkFile,
|
||||
getDownloadArchiveExtension,
|
||||
isVersionSatisfies,
|
||||
renameWinArchive
|
||||
} from '../../util.js';
|
||||
import * as core from '@actions/core';
|
||||
@@ -66,8 +65,8 @@ export class LibericaDistributions extends JavaBase {
|
||||
}));
|
||||
|
||||
const satisfiedVersion = availableVersions
|
||||
.filter(item => isVersionSatisfies(range, item.version))
|
||||
.sort((a, b) => -semver.compareBuild(a.version, b.version))[0];
|
||||
.filter(item => this.isVersionSatisfied(range, item.version))
|
||||
.sort((a, b) => -this.compareVersions(a.version, b.version))[0];
|
||||
|
||||
if (!satisfiedVersion) {
|
||||
const availableVersionStrings = availableVersions.map(
|
||||
@@ -79,6 +78,65 @@ export class LibericaDistributions extends JavaBase {
|
||||
return satisfiedVersion;
|
||||
}
|
||||
|
||||
protected normalizeVersion(version: string) {
|
||||
const normalized = super.normalizeVersion(version);
|
||||
if (/^\d+\.\d+\.\d+(?:\.\d+)+(?:-ea)?$/.test(version)) {
|
||||
// Keep patch-only requests distinct from exact SDKMAN build requests.
|
||||
normalized.version = version.replace(/-ea$/, '');
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
protected isVersionSatisfied(range: string, version: string): boolean {
|
||||
const patchRequest = range.match(/^(\d+\.\d+\.\d+)((?:\.\d+)+)$/);
|
||||
if (patchRequest) {
|
||||
const candidate = semver.parse(version);
|
||||
if (!candidate || !semver.eq(patchRequest[1], candidate)) {
|
||||
return false;
|
||||
}
|
||||
const patch =
|
||||
candidate.build.length > 1
|
||||
? candidate.build.slice(0, -1).join('.')
|
||||
: '0';
|
||||
return patch === patchRequest[2].slice(1);
|
||||
}
|
||||
return super.isVersionSatisfied(range, version);
|
||||
}
|
||||
|
||||
protected compareVersions(a: string, b: string): number {
|
||||
const mainComparison = semver.compare(a, b);
|
||||
if (mainComparison !== 0) {
|
||||
return mainComparison;
|
||||
}
|
||||
|
||||
const getPatchAndBuild = (version: string) => {
|
||||
const build = new semver.SemVer(version).build;
|
||||
return build.length > 1
|
||||
? {patch: build.slice(0, -1), build: build[build.length - 1]}
|
||||
: {patch: [], build: build[0] ?? '0'};
|
||||
};
|
||||
const first = getPatchAndBuild(a);
|
||||
const second = getPatchAndBuild(b);
|
||||
const compareNumericIdentifiers = (left: string, right: string) =>
|
||||
semver.compareBuild(`0.0.0+${left}`, `0.0.0+${right}`);
|
||||
|
||||
for (
|
||||
let index = 0;
|
||||
index < Math.max(first.patch.length, second.patch.length);
|
||||
index++
|
||||
) {
|
||||
const patchComparison = compareNumericIdentifiers(
|
||||
first.patch[index] ?? '0',
|
||||
second.patch[index] ?? '0'
|
||||
);
|
||||
if (patchComparison !== 0) {
|
||||
return patchComparison;
|
||||
}
|
||||
}
|
||||
|
||||
return compareNumericIdentifiers(first.build, second.build);
|
||||
}
|
||||
|
||||
private async getAvailableVersions(): Promise<LibericaVersion[]> {
|
||||
if (core.isDebug()) {
|
||||
console.time('Retrieving available versions for Liberica took'); // eslint-disable-line no-console
|
||||
@@ -166,11 +224,19 @@ export class LibericaDistributions extends JavaBase {
|
||||
}
|
||||
|
||||
private convertVersionToSemver(version: LibericaVersion): string {
|
||||
const {buildVersion, featureVersion, interimVersion, updateVersion} =
|
||||
version;
|
||||
const {featureVersion, interimVersion, updateVersion} = version;
|
||||
const buildVersion = version.version.split('+')[1] || version.buildVersion;
|
||||
const mainVersion = [featureVersion, interimVersion, updateVersion].join(
|
||||
'.'
|
||||
);
|
||||
const patchVersion = version.version
|
||||
.split('+')[0]
|
||||
.split('.')
|
||||
.slice(3)
|
||||
.join('.');
|
||||
if (patchVersion) {
|
||||
return `${mainVersion}+${patchVersion}.${buildVersion}`;
|
||||
}
|
||||
if (buildVersion != 0) {
|
||||
return `${mainVersion}+${buildVersion}`;
|
||||
}
|
||||
|
||||
@@ -8,10 +8,12 @@ import {
|
||||
cacheJdkDir,
|
||||
extractJdkFile,
|
||||
getDownloadArchiveExtension,
|
||||
normalizeJavaVersionToSemver,
|
||||
renameWinArchive
|
||||
} from '../../util.js';
|
||||
import * as gpg from '../../gpg.js';
|
||||
import {MICROSOFT_PUBLIC_KEY} from './microsoft-key.js';
|
||||
import bundledVersions from './microsoft-openjdk-versions.json' with {type: 'json'};
|
||||
import {SIGNATURE_VERIFICATION_FAILURE_HELP} from '../../constants.js';
|
||||
import * as core from '@actions/core';
|
||||
import * as tc from '@actions/tool-cache';
|
||||
@@ -108,7 +110,20 @@ export class MicrosoftDistributions extends JavaBase {
|
||||
throw new Error('Could not load manifest for Microsoft Build of OpenJDK');
|
||||
}
|
||||
|
||||
const foundRelease = await tc.findFromManifest(range, true, manifest, arch);
|
||||
const findMatchingRelease = async (releases: tc.IToolRelease[]) => {
|
||||
const matchingReleases = releases
|
||||
.map(release => ({
|
||||
...release,
|
||||
version: normalizeJavaVersionToSemver(release.version)
|
||||
}))
|
||||
.filter(release => this.isVersionSatisfied(range, release.version));
|
||||
|
||||
return tc.findFromManifest(range, true, matchingReleases, arch);
|
||||
};
|
||||
|
||||
const foundRelease =
|
||||
(await findMatchingRelease(manifest)) ??
|
||||
(await findMatchingRelease(bundledVersions));
|
||||
|
||||
if (!foundRelease) {
|
||||
const availableVersionStrings = manifest.map(item => item.version);
|
||||
|
||||
@@ -1,4 +1,47 @@
|
||||
[
|
||||
{
|
||||
"version": "25.0.5+0.2",
|
||||
"stable": true,
|
||||
"release_url": "https://aka.ms/download-jdk",
|
||||
"files": [
|
||||
{
|
||||
"filename": "microsoft-jdk-25.0.5.0.2-macos-x64.tar.gz",
|
||||
"arch": "x64",
|
||||
"platform": "darwin",
|
||||
"download_url": "https://aka.ms/download-jdk/microsoft-jdk-25.0.5.0.2-macos-x64.tar.gz"
|
||||
},
|
||||
{
|
||||
"filename": "microsoft-jdk-25.0.5.0.2-linux-x64.tar.gz",
|
||||
"arch": "x64",
|
||||
"platform": "linux",
|
||||
"download_url": "https://aka.ms/download-jdk/microsoft-jdk-25.0.5.0.2-linux-x64.tar.gz"
|
||||
},
|
||||
{
|
||||
"filename": "microsoft-jdk-25.0.5.0.2-windows-x64.zip",
|
||||
"arch": "x64",
|
||||
"platform": "win32",
|
||||
"download_url": "https://aka.ms/download-jdk/microsoft-jdk-25.0.5.0.2-windows-x64.zip"
|
||||
},
|
||||
{
|
||||
"filename": "microsoft-jdk-25.0.5.0.2-macos-aarch64.tar.gz",
|
||||
"arch": "aarch64",
|
||||
"platform": "darwin",
|
||||
"download_url": "https://aka.ms/download-jdk/microsoft-jdk-25.0.5.0.2-macos-aarch64.tar.gz"
|
||||
},
|
||||
{
|
||||
"filename": "microsoft-jdk-25.0.5.0.2-linux-aarch64.tar.gz",
|
||||
"arch": "aarch64",
|
||||
"platform": "linux",
|
||||
"download_url": "https://aka.ms/download-jdk/microsoft-jdk-25.0.5.0.2-linux-aarch64.tar.gz"
|
||||
},
|
||||
{
|
||||
"filename": "microsoft-jdk-25.0.5.0.2-windows-aarch64.zip",
|
||||
"arch": "aarch64",
|
||||
"platform": "win32",
|
||||
"download_url": "https://aka.ms/download-jdk/microsoft-jdk-25.0.5.0.2-windows-aarch64.zip"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "25.0.3",
|
||||
"stable": true,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import semver from 'semver';
|
||||
import {convertVersionToSemver} from '../util.js';
|
||||
import {normalizeJavaVersionToSemver} from '../util.js';
|
||||
|
||||
export enum JavaDistribution {
|
||||
Temurin = 'temurin',
|
||||
@@ -98,12 +98,9 @@ function canResolveTemurinJmods(version: string): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
let normalizedRange = normalizedVersion
|
||||
.replace(/-ea$/, '')
|
||||
.replace('-ea.', '+');
|
||||
if (/^\d+(\.\d+){3,}$/.test(normalizedRange)) {
|
||||
normalizedRange = convertVersionToSemver(normalizedRange);
|
||||
}
|
||||
const normalizedRange = normalizeJavaVersionToSemver(
|
||||
normalizedVersion.replace(/-ea$/, '').replace('-ea.', '+')
|
||||
);
|
||||
if (!semver.validRange(normalizedRange)) {
|
||||
// JavaBase owns general version validation and its targeted error messages.
|
||||
return true;
|
||||
|
||||
@@ -5,7 +5,14 @@ import {JavaDistribution} from './package-types.js';
|
||||
export type JavaPlatform = 'linux' | 'macos' | 'windows' | 'solaris';
|
||||
|
||||
export type JavaArchitecture =
|
||||
'x86' | 'x64' | 'armv7' | 'aarch64' | 'ppc64le' | 'ppc64' | 's390x';
|
||||
| 'x86'
|
||||
| 'x64'
|
||||
| 'armv7'
|
||||
| 'aarch64'
|
||||
| 'ppc64le'
|
||||
| 'ppc64'
|
||||
| 'riscv64'
|
||||
| 's390x';
|
||||
|
||||
interface VersionedArchitecture {
|
||||
architecture: JavaArchitecture;
|
||||
@@ -27,7 +34,14 @@ export type JavaPlatformCapability =
|
||||
RestrictedPlatformCapability | UnrestrictedPlatformCapability;
|
||||
|
||||
const X64_ARM64 = ['x64', 'aarch64'] as const;
|
||||
const STANDARD_LINUX = ['x64', 'x86', 'aarch64', 'ppc64le', 's390x'] as const;
|
||||
const STANDARD_LINUX = [
|
||||
'x64',
|
||||
'x86',
|
||||
'aarch64',
|
||||
'ppc64le',
|
||||
'riscv64',
|
||||
's390x'
|
||||
] as const;
|
||||
|
||||
export const JAVA_PLATFORM_CAPABILITIES: Record<
|
||||
JavaDistribution,
|
||||
@@ -174,9 +188,13 @@ const CANONICAL_ARCHITECTURES: readonly JavaArchitecture[] = [
|
||||
'aarch64',
|
||||
'ppc64le',
|
||||
'ppc64',
|
||||
'riscv64',
|
||||
's390x'
|
||||
];
|
||||
|
||||
// Used in environment-variable names and tool-cache paths.
|
||||
const ARCHITECTURE_TOKEN = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
|
||||
|
||||
const PLATFORM_ALIASES: Readonly<
|
||||
Partial<Record<NodeJS.Platform, JavaPlatform>>
|
||||
> = {
|
||||
@@ -189,14 +207,27 @@ const PLATFORM_ALIASES: Readonly<
|
||||
export function normalizeArchitecture(architecture: string): string {
|
||||
const trimmedArchitecture = architecture.trim();
|
||||
const normalizedArchitecture = trimmedArchitecture.toLowerCase();
|
||||
return (
|
||||
ARCHITECTURE_ALIASES[normalizedArchitecture] ??
|
||||
const alias = Object.prototype.hasOwnProperty.call(
|
||||
ARCHITECTURE_ALIASES,
|
||||
normalizedArchitecture
|
||||
)
|
||||
? ARCHITECTURE_ALIASES[normalizedArchitecture]
|
||||
: undefined;
|
||||
const result =
|
||||
alias ??
|
||||
(CANONICAL_ARCHITECTURES.includes(
|
||||
normalizedArchitecture as JavaArchitecture
|
||||
)
|
||||
? normalizedArchitecture
|
||||
: trimmedArchitecture)
|
||||
: trimmedArchitecture);
|
||||
|
||||
if (!ARCHITECTURE_TOKEN.test(result)) {
|
||||
throw new Error(
|
||||
`Unsupported architecture value: ${JSON.stringify(architecture)}`
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function normalizePlatform(
|
||||
|
||||
@@ -70,7 +70,7 @@ export class TemurinDistribution extends JavaBase {
|
||||
const formattedVersion = this.stable
|
||||
? item.version_data.semver
|
||||
: item.version_data.semver.replace('-beta+', '+');
|
||||
return {
|
||||
const release: JavaDownloadRelease = {
|
||||
version: formattedVersion,
|
||||
url: item.binaries[0].package.link,
|
||||
signatureUrl: item.binaries[0].package.signature_link,
|
||||
@@ -79,11 +79,29 @@ export class TemurinDistribution extends JavaBase {
|
||||
value: item.binaries[0].package.checksum,
|
||||
source: item.binaries[0].package.checksum_link
|
||||
}
|
||||
} as JavaDownloadRelease;
|
||||
};
|
||||
return {
|
||||
release,
|
||||
openjdkVersion: getOpenJdkSemverVersion(item.version_data)
|
||||
};
|
||||
});
|
||||
|
||||
// The Adoptium API `semver` folds the JEP 322 patch field into the build
|
||||
// number ('26.0.2.1+1' -> '26.0.2+101') and appends extra metadata for LTS
|
||||
// releases ('25.0.4+7' -> '25.0.4+7.0.LTS'). Exact versions requested by
|
||||
// users follow the OpenJDK notation instead, so also match them against a
|
||||
// key derived from the OpenJDK version fields ('26.0.2+1.1', '25.0.4+7').
|
||||
const isExactBuildRequest = (semver.parse(version)?.build.length ?? 0) > 0;
|
||||
|
||||
const satisfiedVersions = availableVersionsWithBinaries
|
||||
.filter(item => isVersionSatisfies(version, item.version))
|
||||
.filter(
|
||||
({release, openjdkVersion}) =>
|
||||
isVersionSatisfies(version, release.version) ||
|
||||
(isExactBuildRequest &&
|
||||
openjdkVersion !== null &&
|
||||
semver.compareBuild(version, openjdkVersion) === 0)
|
||||
)
|
||||
.map(({release}) => release)
|
||||
.sort((a, b) => {
|
||||
return -semver.compareBuild(a.version, b.version);
|
||||
});
|
||||
@@ -92,7 +110,7 @@ export class TemurinDistribution extends JavaBase {
|
||||
satisfiedVersions.length > 0 ? satisfiedVersions[0] : null;
|
||||
if (!resolvedFullVersion) {
|
||||
const availableVersionStrings = availableVersionsWithBinaries.map(
|
||||
item => item.version
|
||||
({release}) => release.version
|
||||
);
|
||||
throw this.createVersionNotFoundError(version, availableVersionStrings);
|
||||
}
|
||||
@@ -309,3 +327,20 @@ export class TemurinDistribution extends JavaBase {
|
||||
return architecture === 'armv7' ? 'arm' : architecture;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a SemVer version from the OpenJDK version fields reported by the
|
||||
* Adoptium API, e.g. '26.0.2.1+1' -> '26.0.2+1.1' and '25.0.4+7-LTS' ->
|
||||
* '25.0.4+7'. Returns null if the fields cannot form a valid SemVer version.
|
||||
*/
|
||||
function getOpenJdkSemverVersion(
|
||||
versionData: ITemurinAvailableVersions['version_data']
|
||||
): string | null {
|
||||
const {major, minor, security, patch, build} = versionData;
|
||||
if (build === undefined || build === null) {
|
||||
return null;
|
||||
}
|
||||
const buildMetadata = patch ? `${patch}.${build}` : `${build}`;
|
||||
const version = `${major}.${minor}.${security}+${buildMetadata}`;
|
||||
return semver.valid(version) ? version : null;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ export interface ITemurinAvailableVersions {
|
||||
minor: number;
|
||||
openjdk_version: string;
|
||||
security: string;
|
||||
patch?: number;
|
||||
semver: string;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import * as core from '@actions/core';
|
||||
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import semver from 'semver';
|
||||
|
||||
import {JavaBase} from '../base-installer.js';
|
||||
import {IZuluPackageDetails, IZuluVersions} from './models.js';
|
||||
@@ -30,6 +29,17 @@ interface ZuluResolvedRelease {
|
||||
packageUuid: string;
|
||||
}
|
||||
|
||||
function compareNumberArrays(a: number[], b: number[]): number {
|
||||
const length = Math.max(a.length, b.length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
const diff = (a[i] ?? 0) - (b[i] ?? 0);
|
||||
if (diff !== 0) {
|
||||
return diff;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export class ZuluDistribution extends JavaBase {
|
||||
constructor(installerOptions: JavaInstallerOptions) {
|
||||
super('Zulu', installerOptions);
|
||||
@@ -50,7 +60,9 @@ export class ZuluDistribution extends JavaBase {
|
||||
return {
|
||||
version: convertVersionToSemver(javaVersion),
|
||||
url: item.download_url,
|
||||
zuluVersion: convertVersionToSemver(item.distro_version),
|
||||
javaVersion: item.java_version,
|
||||
buildNumber: item.openjdk_build_number ?? 0,
|
||||
distroVersion: item.distro_version,
|
||||
packageUuid: item.package_uuid
|
||||
};
|
||||
});
|
||||
@@ -58,11 +70,14 @@ export class ZuluDistribution extends JavaBase {
|
||||
const satisfiedVersions = availableVersions
|
||||
.filter(item => isVersionSatisfies(version, item.version))
|
||||
.sort((a, b) => {
|
||||
// Azul provides two versions: java_version and distro_version
|
||||
// we should sort by both fields by descending
|
||||
// Compare numerically rather than via semver build metadata: Azul
|
||||
// hotfix releases carry a 4th java_version segment (e.g. 25.0.4.1+1,
|
||||
// rendered as '25.0.4+1.1') that must rank above 25.0.4+7, whereas
|
||||
// semver.compareBuild would order the build identifiers '7' > '1'.
|
||||
return (
|
||||
-semver.compareBuild(a.version, b.version) ||
|
||||
-semver.compareBuild(a.zuluVersion, b.zuluVersion)
|
||||
-compareNumberArrays(a.javaVersion, b.javaVersion) ||
|
||||
b.buildNumber - a.buildNumber ||
|
||||
-compareNumberArrays(a.distroVersion, b.distroVersion)
|
||||
);
|
||||
})
|
||||
.map((item): ZuluResolvedRelease => ({
|
||||
|
||||
+7
-3
@@ -89,17 +89,20 @@ export async function removeGpgHome(gpgHome: string): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
await stopGpgAgent(resolvedGpgHome);
|
||||
await io.rmRF(resolvedGpgHome);
|
||||
}
|
||||
|
||||
async function stopGpgAgent(gpgHome: string): Promise<void> {
|
||||
try {
|
||||
await exec.exec(
|
||||
'gpgconf',
|
||||
['--homedir', toGpgPath(resolvedGpgHome), '--kill', 'gpg-agent'],
|
||||
['--homedir', toGpgPath(gpgHome), '--kill', 'gpg-agent'],
|
||||
{silent: true, ignoreReturnCode: true}
|
||||
);
|
||||
} catch {
|
||||
// gpgconf may be unavailable, but directory removal must still be attempted.
|
||||
}
|
||||
|
||||
await io.rmRF(resolvedGpgHome);
|
||||
}
|
||||
|
||||
export async function verifyPackageSignature(
|
||||
@@ -160,6 +163,7 @@ export async function verifyPackageSignature(
|
||||
options
|
||||
);
|
||||
} finally {
|
||||
await stopGpgAgent(gpgHome);
|
||||
await io.rmRF(signaturePath);
|
||||
await io.rmRF(gpgHome);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import {INPUT_PROBLEM_MATCHER} from './constants.js';
|
||||
import {getBooleanInput} from './util.js';
|
||||
|
||||
export function configureProblemMatcher(matcherPath: string): void {
|
||||
const problemMatcherEnabled = getBooleanInput(INPUT_PROBLEM_MATCHER, true);
|
||||
const problemMatcherEnabled = getBooleanInput(INPUT_PROBLEM_MATCHER, false);
|
||||
|
||||
if (!problemMatcherEnabled) {
|
||||
core.debug('Java problem matcher is disabled');
|
||||
|
||||
+34
@@ -397,6 +397,18 @@ export function getVersionFromFileContent(
|
||||
const tentativeVersion = avoidOldNotation(capturedVersion);
|
||||
const rawVersion = tentativeVersion.split('-')[0];
|
||||
|
||||
// Preserve Corretto's full vendor release before generic coercion drops its extra numeric fields.
|
||||
if (
|
||||
(extractedDistribution || distributionName) === 'corretto' &&
|
||||
capturedVersion === tentativeVersion &&
|
||||
isExactCorrettoVersion(tentativeVersion)
|
||||
) {
|
||||
return {
|
||||
version: tentativeVersion,
|
||||
distribution: extractedDistribution
|
||||
};
|
||||
}
|
||||
|
||||
let version = semver.validRange(rawVersion)
|
||||
? tentativeVersion
|
||||
: semver.coerce(tentativeVersion);
|
||||
@@ -500,6 +512,13 @@ function avoidOldNotation(content: string): string {
|
||||
return content.startsWith('1.') ? content.substring(2) : content;
|
||||
}
|
||||
|
||||
export function isExactCorrettoVersion(version: string): boolean {
|
||||
// Java 8 releases have a nonzero update field; later releases use five numeric fields.
|
||||
return /^(?:8\.(?!0+(?:\.|$))\d+(?:\.\d+){2}|(?!8\.)\d+(?:\.\d+){4})$/.test(
|
||||
version.replace('+', '.')
|
||||
);
|
||||
}
|
||||
|
||||
export function convertVersionToSemver(version: number[] | string) {
|
||||
// Some distributions may use semver-like notation (12.10.2.1, 12.10.2.1.1)
|
||||
const versionArray = Array.isArray(version) ? version : version.split('.');
|
||||
@@ -510,6 +529,21 @@ export function convertVersionToSemver(version: number[] | string) {
|
||||
return mainVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Java versions (JEP 322) can contain more numeric fields than SemVer allows,
|
||||
* e.g. '11.0.9.1' or Temurin respins such as '26.0.2.1+1'. Move the extra
|
||||
* fields into SemVer build metadata ('11.0.9+1', '26.0.2+1.1'). Any other
|
||||
* input (ranges, regular SemVer versions) is returned unchanged.
|
||||
*/
|
||||
export function normalizeJavaVersionToSemver(version: string): string {
|
||||
const match = /^(\d+(?:\.\d+){3,})(?:\+([0-9A-Za-z.-]+))?$/.exec(version);
|
||||
if (!match) {
|
||||
return version;
|
||||
}
|
||||
const converted = convertVersionToSemver(match[1]);
|
||||
return match[2] ? `${converted}.${match[2]}` : converted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a validator for the bytes currently served by a URL from the response
|
||||
* headers of a HEAD request. A vendor's `/latest/` URL never changes, so this
|
||||
|
||||
Reference in New Issue
Block a user