From 4baa9b45d2bff6fcbef9619dabd0cb1ca822905b Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Jun 2026 17:59:24 -0400 Subject: [PATCH 1/8] docs: replace non-existent HelloWorldApp references with java --version (#1043) * Initial plan * docs: replace HelloWorldApp references with java --version in README and advanced-usage --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- README.md | 8 ++++---- docs/advanced-usage.md | 40 ++++++++++++++++++++-------------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index c4809a82..03860b37 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ steps: with: distribution: 'temurin' # See 'Supported distributions' for available options java-version: '25' -- run: java HelloWorldApp.java +- run: java --version ``` #### Azul Zulu OpenJDK @@ -87,7 +87,7 @@ steps: with: distribution: 'zulu' # See 'Supported distributions' for available options java-version: '25' -- run: java HelloWorldApp.java +- run: java --version ``` #### Supported version syntax @@ -221,7 +221,7 @@ steps: distribution: 'temurin' java-version: '25' check-latest: true -- run: java HelloWorldApp.java +- run: java --version ``` ### Testing against different Java versions @@ -240,7 +240,7 @@ jobs: with: distribution: '' java-version: ${{ matrix.java }} - - run: java HelloWorldApp.java + - run: java --version ``` ### Install multiple JDKs diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 1b1e4fee..4aeca46c 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -36,7 +36,7 @@ steps: with: distribution: 'temurin' java-version: '21' -- run: java -cp java HelloWorldApp +- run: java --version ``` ### Adopt @@ -49,7 +49,7 @@ steps: with: distribution: 'adopt-hotspot' java-version: '11' -- run: java -cp java HelloWorldApp +- run: java --version ``` ### Zulu @@ -62,7 +62,7 @@ steps: distribution: 'zulu' java-version: '21' java-package: jdk # optional (jdk, jre, jdk+fx or jre+fx) - defaults to jdk -- run: java -cp java HelloWorldApp +- run: java --version ``` ### Liberica @@ -75,7 +75,7 @@ steps: distribution: 'liberica' java-version: '21' java-package: jdk # optional (jdk, jre, jdk+fx or jre+fx) - defaults to jdk -- run: java -cp java HelloWorldApp +- run: java --version ``` ### Microsoft @@ -87,7 +87,7 @@ steps: with: distribution: 'microsoft' java-version: '21' -- run: java -cp java HelloWorldApp +- run: java --version ``` ### Using Microsoft distribution on GHES @@ -116,7 +116,7 @@ steps: with: distribution: 'corretto' java-version: '21' -- run: java -cp java HelloWorldApp +- run: java --version ``` ### Oracle @@ -129,7 +129,7 @@ steps: with: distribution: 'oracle' java-version: '21' -- run: java -cp java HelloWorldApp +- run: java --version ``` ### Alibaba Dragonwell @@ -142,7 +142,7 @@ steps: with: distribution: 'dragonwell' java-version: '8' -- run: java -cp java HelloWorldApp +- run: java --version ``` ### SapMachine @@ -154,7 +154,7 @@ steps: with: distribution: 'sapmachine' java-version: '21' -- run: java -cp java HelloWorldApp +- run: java --version ``` ### GraalVM @@ -168,8 +168,8 @@ steps: distribution: 'graalvm' java-version: '21' - run: | - java -cp java HelloWorldApp - native-image -cp java HelloWorldApp + java --version + native-image --version ``` ### JetBrains @@ -186,7 +186,7 @@ steps: with: distribution: 'jetbrains' java-version: '11' -- run: java -cp java HelloWorldApp +- run: java --version ``` The JetBrains installer uses the GitHub API to fetch the latest version. If you believe your project is going to be running into rate limits, you can provide a @@ -202,7 +202,7 @@ steps: java-package: 'jdk' # optional (jdk, jre, jdk+jcef, jre+jcef, jdk+ft, or jre+ft) - defaults to jdk env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -- run: java -cp java HelloWorldApp +- run: java --version ``` You can specify your package type (as shown in the [releases page](https://github.com/JetBrains/JetBrainsRuntime/releases/)) in the `java-package` parameter. @@ -225,7 +225,7 @@ steps: distribution: '' java-version: '11' java-package: jdk # optional (jdk or jre) - defaults to jdk -- run: java -cp java HelloWorldApp +- run: java --version ``` ## Installing custom Java architecture @@ -238,7 +238,7 @@ steps: distribution: '' java-version: '11' architecture: x86 # optional - default value derived from the runner machine -- run: java -cp java HelloWorldApp +- run: java --version ``` ## Installing Java from local file @@ -256,7 +256,7 @@ steps: java-version: '11.0.0' architecture: x64 -- run: java -cp java HelloWorldApp +- run: java --version ``` If your use-case requires a custom distribution (in the example, alpine-linux is used) or a version that is not provided by setup-java and you want to always install the latest version during runtime, then you can use the following code to auto-download the latest JDK, determine the semver needed for setup-java, and setup-java will take care of the installation and caching on the VM: @@ -281,7 +281,7 @@ If your use-case requires a custom distribution (in the example, alpine-linux is jdkFile: ${{ runner.temp }}/java_package.tar.gz java-version: {{ steps.fetch_latest_jdk.outputs.java_version }} architecture: x64 - - run: java -cp java HelloWorldApp + - run: java --version ``` ## Testing against different Java distributions @@ -302,7 +302,7 @@ jobs: with: distribution: ${{ matrix.distribution }} java-version: ${{ matrix.java }} - - run: java -cp java HelloWorldApp + - run: java --version ``` #### Testing against different platforms @@ -322,7 +322,7 @@ jobs: with: distribution: 'temurin' java-version: ${{ matrix.java }} - - run: java -cp java HelloWorldApp + - run: java --version ``` ## Publishing using Apache Maven @@ -580,7 +580,7 @@ steps: distribution: 'temurin' java-version: '11' mvn-toolchain-id: 'some_other_id' -- run: java -cp java HelloWorldApp +- run: java --version ``` In case you install multiple versions of Java at once you can use the same syntax as used in `java-versions`. Please note that you have to declare an ID for all Java versions that will be installed or the `mvn-toolchain-id` instruction will be skipped wholesale due to mapping ambiguities. From 5431e71f9a4e00431c1c904af57e62794b518b11 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Jun 2026 18:04:38 -0400 Subject: [PATCH 2/8] docs: add JavaFX Maven project configuration instructions (#1044) * Initial plan * docs: add JavaFX Maven project configuration instructions --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Bruno Borges --- docs/advanced-usage.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 4aeca46c..375ea7ec 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -12,6 +12,7 @@ - [GraalVM](#GraalVM) - [JetBrains](#JetBrains) - [Installing custom Java package type](#Installing-custom-Java-package-type) + - [JavaFX Maven project](#JavaFX-Maven-project) - [Installing custom Java architecture](#Installing-custom-Java-architecture) - [Installing custom Java distribution from local file](#Installing-Java-from-local-file) - [Testing against different Java distributions](#Testing-against-different-Java-distributions) @@ -228,6 +229,30 @@ steps: - run: java --version ``` +### JavaFX Maven project + +For JavaFX projects that use Maven, use `jdk+fx` (or `jre+fx`) as the `java-package` value together with a distribution that supports it (e.g. `zulu` or `liberica`). Then include the [`javafx-maven-plugin`](https://openjfx.io/openjfx-docs/#maven) in your `pom.xml` as described in the [Getting Started with JavaFX](https://openjfx.io/openjfx-docs/#maven) guide. + +```yaml +steps: +- uses: actions/checkout@v6 +- uses: actions/setup-java@v5 + with: + distribution: 'zulu' + java-version: '21' + java-package: jdk+fx + cache: maven +- name: Build with Maven + run: mvn --no-transfer-progress compile +``` + +To run the JavaFX application in CI: + +```yaml +- name: Run with Maven + run: mvn --no-transfer-progress javafx:run +``` + ## Installing custom Java architecture ```yaml From a9a46fbe0996878a5673db759d29dbc5f470320e Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Mon, 22 Jun 2026 21:51:01 -0400 Subject: [PATCH 3/8] docs: document self-signed certificate / internal CA handling for GitHub Enterprise (#1050) Adds an advanced-usage section explaining the 'self signed certificate in certificate chain' error seen on GitHub Enterprise Server and behind TLS-inspecting proxies. Recommends the secure fix of trusting the internal CA via NODE_EXTRA_CA_CERTS (or the OS trust store on self-hosted runners), with a GitHub Enterprise callout, and warns against disabling TLS verification since the JDK download has no checksum fallback. Refs #640 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/advanced-usage.md | 54 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 375ea7ec..4b81c854 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -22,6 +22,7 @@ - [Hosted Tool Cache](#Hosted-Tool-Cache) - [Modifying Maven Toolchains](#Modifying-Maven-Toolchains) - [Java-version file](#Java-version-file) +- [Self-signed certificates and internal CAs (GitHub Enterprise)](#Self-signed-certificates-and-internal-CAs-GitHub-Enterprise) See [action.yml](../action.yml) for more details on task inputs. @@ -660,3 +661,56 @@ If the file contains multiple versions, only the first one will be recognized. ***NOTE***: For the tool-version file, ensure that you use standard semantic versioning (semver) formats, as non-standard formats (such as jetbrains-21b212.1) may not be parsed correctly. Additionally, for complex version strings containing multiple version-like segments (for example, java semeru-openj9-11.0.15+10_openj9-0.32.0), the extraction logic may incorrectly capture the last segment (0.32.0) instead of the main version (11.0.15+10). + +## Self-signed certificates and internal CAs (GitHub Enterprise) + +When `setup-java` dynamically downloads a JDK, it makes HTTPS requests both to fetch the available version metadata and to download the JDK archive. If your runners sit behind a **TLS-inspecting corporate proxy**, or you are on **GitHub Enterprise Server (GHES)** with an internal certificate authority, those requests can fail with an error such as: + +``` +Error: self signed certificate in certificate chain +``` + +This happens because the certificate presented to the runner is signed by an **internal or self-signed CA** that is not part of the runner's default trust store. The download itself is fine — the runner simply cannot verify the certificate chain. + +### Recommended fix: trust your internal CA + +The secure way to resolve this is to make the runner trust your organization's CA, which keeps TLS verification fully enabled. `setup-java` runs on Node.js, which honors the [`NODE_EXTRA_CA_CERTS`](https://nodejs.org/api/cli.html#node_extra_ca_certsfile) environment variable. Point it at your CA bundle (in PEM format) **before** the `actions/setup-java` step: + +```yaml +steps: + # The CA bundle is already present on the runner image in this example. + # Alternatively, write it from a secret in a previous step. + - name: Trust the internal CA + run: echo "NODE_EXTRA_CA_CERTS=/etc/ssl/certs/internal-ca.pem" >> "$GITHUB_ENV" + + - uses: actions/setup-java@v5 + with: + distribution: 'temurin' + java-version: '21' +``` + +If you keep the certificate in a secret rather than on the runner image, write it to disk first: + +```yaml +steps: + - name: Write and trust the internal CA + run: | + echo "${{ secrets.INTERNAL_CA_PEM }}" > "${RUNNER_TEMP}/internal-ca.pem" + echo "NODE_EXTRA_CA_CERTS=${RUNNER_TEMP}/internal-ca.pem" >> "$GITHUB_ENV" + + - uses: actions/setup-java@v5 + with: + distribution: 'temurin' + java-version: '21' +``` + +For **self-hosted runners**, you can instead install your CA into the operating system's trust store (for example, `update-ca-certificates` on Debian/Ubuntu or `update-ca-trust` on RHEL). This makes the certificate trusted for all tooling on the runner, not just `setup-java`. + +### GitHub Enterprise customers + +On **GitHub Enterprise Server**, traffic from your runners frequently passes through an organization-managed proxy or terminates TLS at an appliance using a certificate from an internal CA. If your workflows hit the error above, set `NODE_EXTRA_CA_CERTS` to your enterprise CA bundle (or bake the CA into your self-hosted runner image) as shown above. Coordinate with your platform team to obtain the correct PEM bundle for your appliance and proxy chain. + +### Security warning: do not disable certificate verification + +Do **not** work around this error by disabling TLS verification (for example, by setting `NODE_TLS_REJECT_UNAUTHORIZED=0`). `setup-java` does not verify a pinned checksum or signature of the downloaded archive, so **TLS is effectively the only integrity guarantee** on the JDK download. Disabling verification would expose your workflow to a man-in-the-middle attacker who could serve a tampered JDK — which then becomes the `java` used by the rest of your pipeline, with access to your secrets and credentials. Always extend trust to your CA instead of turning verification off. + From 668c1ea991737ea087e51dbf0bcf7fd41cbc20ee Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Mon, 22 Jun 2026 21:59:01 -0400 Subject: [PATCH 4/8] docs: add post-install keytool import for the JDK cacerts trust store (#1051) Document how to make the installed JDK trust an internal CA at application runtime by importing it into $JAVA_HOME/lib/security/cacerts with keytool after setup-java runs. Clarifies this is the runtime trust layer, distinct from the download/transport layer (NODE_EXTRA_CA_CERTS), and notes hosted vs self-hosted persistence caveats. Refs #640 #1035 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/advanced-usage.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index 4b81c854..f4838b34 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -714,3 +714,41 @@ On **GitHub Enterprise Server**, traffic from your runners frequently passes thr Do **not** work around this error by disabling TLS verification (for example, by setting `NODE_TLS_REJECT_UNAUTHORIZED=0`). `setup-java` does not verify a pinned checksum or signature of the downloaded archive, so **TLS is effectively the only integrity guarantee** on the JDK download. Disabling verification would expose your workflow to a man-in-the-middle attacker who could serve a tampered JDK — which then becomes the `java` used by the rest of your pipeline, with access to your secrets and credentials. Always extend trust to your CA instead of turning verification off. +### Trusting an internal CA inside the installed JDK + +The guidance above makes the **runner** trust your CA so that the JDK can be *downloaded*. That is a separate layer from making the **installed JDK** trust your CA at *application runtime*. If your build steps (Maven/Gradle dependency resolution, integration tests, HTTPS calls from your app, etc.) connect to internal services that present a certificate from your internal CA, the JDK will reject them with errors such as: + +``` +PKIX path building failed: unable to find valid certification path to requested target +``` + +The JDK keeps its own trust store — a keystore named `cacerts` under `$JAVA_HOME/lib/security/cacerts` — which is independent of the operating system and Node trust stores. After `setup-java` has run (so that `JAVA_HOME` points at the freshly installed JDK), import your CA into that keystore with `keytool`: + +```yaml +steps: + - uses: actions/setup-java@v5 + with: + distribution: 'temurin' + java-version: '21' + + - name: Import internal CA into the JDK trust store + shell: bash + run: | + # Write the CA from a secret (or reference a file already on the runner) + echo "${{ secrets.INTERNAL_CA_PEM }}" > "${RUNNER_TEMP}/internal-ca.pem" + keytool -importcert -noprompt \ + -alias internal-ca \ + -file "${RUNNER_TEMP}/internal-ca.pem" \ + -keystore "${JAVA_HOME}/lib/security/cacerts" \ + -storepass changeit +``` + +Notes and caveats: + +- The default keystore password for `cacerts` is `changeit` unless your distribution overrides it. +- On **hosted runners** the change applies only to the current job's JDK and is discarded when the job ends, so include the import step in every job that needs it. +- On **self-hosted runners**, importing into a tool-cache JDK persists for as long as that cached version remains on the runner; if you want it to survive JDK reinstalls, pre-seed the CA into your runner image or re-run the import step each time. +- Prefer giving the certificate a stable, descriptive `-alias` so re-runs are idempotent (re-importing the same alias will fail; add `keytool -delete -alias internal-ca ...` first if you re-run within a long-lived runner). + +This documents the post-install workflow; there is no dedicated action input for supplying a custom `cacerts` file. + From 1d252528046b5ceb47839b03a115e0ea04f026cc Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Tue, 23 Jun 2026 13:10:17 -0400 Subject: [PATCH 5/8] chore: Harden workflows: least-privilege permissions + zizmor integration (#1039) * Harden workflows with least-privilege permissions and zizmor Apply GitHub Actions security best practices to the action's own workflows and integrate zizmor to catch regressions. - Add explicit least-privilege `permissions:` to every workflow (contents: read for read-only workflows; default-deny `{}` with job-scoped grants for codeql, publish-immutable-actions and update-config-files). - Set `persist-credentials: false` on all checkout steps that don't need the GITHUB_TOKEN afterwards. - Move `${{ ... }}` expansions out of `run:` blocks into `env:` vars to avoid template injection. - Pin the alpine container image (alpine:latest -> alpine:3.21). - Add a zizmor CI workflow that uploads SARIF to code scanning, plus a `.github/zizmor.yml` pinning policy (ref-pin for actions/* and github/*, hash-pin for third-party actions). zizmor now reports no findings (offline and online). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Fix indentation of if: in zizmor SARIF upload step The `if:` key on the "Upload SARIF results to code scanning" step had no indentation, producing invalid YAML ("Nested mappings are not allowed in compact mappings"). This broke `npm run format-check` (prettier) in Basic validation. Indent `if:` to 8 spaces so it nests under the step alongside uses/with. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/workflows/basic-validation.yml | 3 + .github/workflows/check-dist.yml | 3 + .github/workflows/codeql-analysis.yml | 2 + .../workflows/e2e-cache-dependency-path.yml | 9 ++ .github/workflows/e2e-cache.yml | 15 +++ .github/workflows/e2e-local-file.yml | 21 +++- .github/workflows/e2e-publishing.yml | 11 +++ .github/workflows/e2e-versions.yml | 99 ++++++++++++++++--- .github/workflows/licensed.yml | 3 + .../workflows/publish-immutable-actions.yml | 4 + .github/workflows/update-config-files.yml | 5 + .github/workflows/zizmor.yml | 48 +++++++++ .github/zizmor.yml | 11 +++ 13 files changed, 215 insertions(+), 19 deletions(-) create mode 100644 .github/workflows/zizmor.yml create mode 100644 .github/zizmor.yml diff --git a/.github/workflows/basic-validation.yml b/.github/workflows/basic-validation.yml index e93e5800..ea70e057 100644 --- a/.github/workflows/basic-validation.yml +++ b/.github/workflows/basic-validation.yml @@ -11,6 +11,9 @@ on: paths-ignore: - '**.md' +permissions: + contents: read + jobs: call-basic-validation: name: Basic validation diff --git a/.github/workflows/check-dist.yml b/.github/workflows/check-dist.yml index 90ef986a..5592249e 100644 --- a/.github/workflows/check-dist.yml +++ b/.github/workflows/check-dist.yml @@ -11,6 +11,9 @@ on: - '**.md' workflow_dispatch: +permissions: + contents: read + jobs: call-check-dist: name: Check dist/ diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 1816c150..598f7de5 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -8,6 +8,8 @@ on: schedule: - cron: '0 3 * * 0' +permissions: {} + jobs: call-codeQL-analysis: permissions: diff --git a/.github/workflows/e2e-cache-dependency-path.yml b/.github/workflows/e2e-cache-dependency-path.yml index 29819855..1c79c081 100644 --- a/.github/workflows/e2e-cache-dependency-path.yml +++ b/.github/workflows/e2e-cache-dependency-path.yml @@ -11,6 +11,9 @@ on: paths-ignore: - '**.md' +permissions: + contents: read + defaults: run: shell: bash @@ -25,6 +28,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Run setup-java with the cache for gradle uses: ./ id: setup-java @@ -52,6 +57,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Run setup-java with the cache for gradle uses: ./ id: setup-java @@ -77,6 +84,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Run setup-java with the cache for gradle uses: ./ id: setup-java diff --git a/.github/workflows/e2e-cache.yml b/.github/workflows/e2e-cache.yml index f0313287..c76cf50b 100644 --- a/.github/workflows/e2e-cache.yml +++ b/.github/workflows/e2e-cache.yml @@ -11,6 +11,9 @@ on: paths-ignore: - '**.md' +permissions: + contents: read + defaults: run: shell: bash @@ -25,6 +28,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Run setup-java with the cache for gradle uses: ./ id: setup-java @@ -51,6 +56,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Run setup-java with the cache for gradle uses: ./ id: setup-java @@ -74,6 +81,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Run setup-java with the cache for maven uses: ./ id: setup-java @@ -98,6 +107,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Run setup-java with the cache for maven uses: ./ id: setup-java @@ -125,6 +136,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Run setup-java with the cache for sbt uses: ./ id: setup-java @@ -175,6 +188,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Run setup-java with the cache for sbt uses: ./ id: setup-java diff --git a/.github/workflows/e2e-local-file.yml b/.github/workflows/e2e-local-file.yml index 313453e1..1b9c4864 100644 --- a/.github/workflows/e2e-local-file.yml +++ b/.github/workflows/e2e-local-file.yml @@ -11,6 +11,9 @@ on: paths-ignore: - '**.md' +permissions: + contents: read + jobs: setup-java-local-file-adopt: name: Validate installation from local file Adopt @@ -22,6 +25,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Download Adopt OpenJDK file run: | if ($IsLinux) { @@ -46,7 +51,9 @@ jobs: java-version: '11.0.0-ea' architecture: x64 - name: Verify Java version - run: bash __tests__/verify-java.sh "11.0.10" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "11.0.10" "$JAVA_PATH" shell: bash setup-java-local-file-zulu: @@ -59,6 +66,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Download Zulu OpenJDK file run: | if ($IsLinux) { @@ -83,7 +92,9 @@ jobs: java-version: '11.0.0-ea' architecture: x64 - name: Verify Java version - run: bash __tests__/verify-java.sh "11.0" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "11.0" "$JAVA_PATH" shell: bash setup-java-local-file-temurin: @@ -96,6 +107,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Download Eclipse Temurin file run: | if ($IsLinux) { @@ -120,5 +133,7 @@ jobs: java-version: '11.0.0-ea' architecture: x64 - name: Verify Java version - run: bash __tests__/verify-java.sh "11.0.12" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "11.0.12" "$JAVA_PATH" shell: bash diff --git a/.github/workflows/e2e-publishing.yml b/.github/workflows/e2e-publishing.yml index e685c43a..02a6b259 100644 --- a/.github/workflows/e2e-publishing.yml +++ b/.github/workflows/e2e-publishing.yml @@ -11,6 +11,9 @@ on: paths-ignore: - '**.md' +permissions: + contents: read + defaults: run: shell: pwsh @@ -26,6 +29,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: setup-java uses: ./ id: setup-java @@ -61,6 +66,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Create fake settings.xml run: | $xmlDirectory = Join-Path $HOME ".m2" @@ -97,6 +104,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Create fake settings.xml run: | $xmlDirectory = Join-Path $HOME ".m2" @@ -134,6 +143,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: setup-java uses: ./ id: setup-java diff --git a/.github/workflows/e2e-versions.yml b/.github/workflows/e2e-versions.yml index 4cc66670..c384d402 100644 --- a/.github/workflows/e2e-versions.yml +++ b/.github/workflows/e2e-versions.yml @@ -13,6 +13,10 @@ on: schedule: - cron: '0 */12 * * *' workflow_dispatch: + +permissions: + contents: read + jobs: setup-java-major-versions: name: ${{ matrix.distribution }} ${{ matrix.version }} (jdk-x64) - ${{ matrix.os }} @@ -74,6 +78,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: setup-java uses: ./ id: setup-java @@ -83,14 +89,17 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Verify Java - run: bash __tests__/verify-java.sh "${{ matrix.version }}" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_VERSION: ${{ matrix.version }} + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "$JAVA_VERSION" "$JAVA_PATH" shell: bash setup-java-alpine-linux: name: ${{ matrix.distribution }} ${{ matrix.version }} (jdk-x64) - alpine-linux - ${{ matrix.os }} runs-on: ${{ matrix.os }} container: - image: alpine:latest + image: alpine:3.21 strategy: fail-fast: false matrix: @@ -100,6 +109,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Install bash run: apk add --no-cache bash - name: setup-java @@ -109,7 +120,10 @@ jobs: java-version: ${{ matrix.version }} distribution: ${{ matrix.distribution }} - name: Verify Java - run: bash __tests__/verify-java.sh "${{ matrix.version }}" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_VERSION: ${{ matrix.version }} + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "$JAVA_VERSION" "$JAVA_PATH" shell: bash setup-java-major-minor-versions: @@ -150,6 +164,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: setup-java uses: ./ id: setup-java @@ -157,10 +173,12 @@ jobs: java-version: ${{ matrix.version }} distribution: ${{ matrix.distribution }} - name: Verify Java - run: bash __tests__/verify-java.sh "${{ matrix.version }}" "${{ steps.setup-java.outputs.path }}" - shell: bash env: + JAVA_VERSION: ${{ matrix.version }} + JAVA_PATH: ${{ steps.setup-java.outputs.path }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: bash __tests__/verify-java.sh "$JAVA_VERSION" "$JAVA_PATH" + shell: bash setup-java-check-latest: name: ${{ matrix.distribution }} ${{ matrix.version }} - check-latest flag - ${{ matrix.os }} @@ -185,6 +203,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: setup-java uses: ./ id: setup-java @@ -195,7 +215,9 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Verify Java - run: bash __tests__/verify-java.sh "11" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "11" "$JAVA_PATH" shell: bash setup-java-multiple-jdks: @@ -221,6 +243,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: setup-java uses: ./ id: setup-java @@ -245,7 +269,9 @@ jobs: } shell: pwsh - name: Verify Java - run: bash __tests__/verify-java.sh "17" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "17" "$JAVA_PATH" shell: bash setup-java-ea-versions-zulu: @@ -260,6 +286,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: setup-java uses: ./ id: setup-java @@ -267,7 +295,10 @@ jobs: java-version: ${{ matrix.version }} distribution: zulu - name: Verify Java - run: bash __tests__/verify-java.sh "${{ matrix.version }}" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_VERSION: ${{ matrix.version }} + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "$JAVA_VERSION" "$JAVA_PATH" shell: bash setup-java-ea-versions-temurin: @@ -282,6 +313,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: setup-java uses: ./ id: setup-java @@ -289,7 +322,10 @@ jobs: java-version: ${{ matrix.version }} distribution: temurin - name: Verify Java - run: bash __tests__/verify-java.sh "${{ matrix.version }}" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_VERSION: ${{ matrix.version }} + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "$JAVA_VERSION" "$JAVA_PATH" shell: bash setup-java-ea-versions-sapmachine: @@ -304,6 +340,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: setup-java uses: ./ id: setup-java @@ -311,7 +349,10 @@ jobs: java-version: ${{ matrix.version }} distribution: sapmachine - name: Verify Java - run: bash __tests__/verify-java.sh "${{ matrix.version }}" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_VERSION: ${{ matrix.version }} + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "$JAVA_VERSION" "$JAVA_PATH" shell: bash setup-java-custom-package-type: @@ -391,6 +432,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: setup-java uses: ./ id: setup-java @@ -401,7 +444,10 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Verify Java - run: bash __tests__/verify-java.sh "${{ matrix.version }}" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_VERSION: ${{ matrix.version }} + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "$JAVA_VERSION" "$JAVA_PATH" shell: bash # Only Liberica and Zulu provide x86 @@ -419,6 +465,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: setup-java uses: ./ id: setup-java @@ -427,7 +475,10 @@ jobs: java-version: ${{ matrix.version }} architecture: 'x86' - name: Verify Java - run: bash __tests__/verify-java.sh "${{ matrix.version }}" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_VERSION: ${{ matrix.version }} + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "$JAVA_VERSION" "$JAVA_PATH" shell: bash setup-java-version-both-version-inputs-presents: @@ -442,6 +493,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Create .java-version file shell: bash run: echo "17" > .java-version @@ -456,7 +509,9 @@ jobs: java-version: 11 java-version-file: ${{matrix.java-version-file }} - name: Verify Java - run: bash __tests__/verify-java.sh "11" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "11" "$JAVA_PATH" shell: bash setup-java-version-from-file-major-notation: @@ -471,6 +526,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Create .java-version file shell: bash run: echo "11" > .java-version @@ -484,7 +541,9 @@ jobs: distribution: ${{ matrix.distribution }} java-version-file: ${{matrix.java-version-file }} - name: Verify Java - run: bash __tests__/verify-java.sh "11" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "11" "$JAVA_PATH" shell: bash setup-java-version-from-file-major-minor-patch-notation: @@ -499,6 +558,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Create .java-version file shell: bash run: echo "17.0.10" > .java-version @@ -512,7 +573,9 @@ jobs: distribution: ${{ matrix.distribution }} java-version-file: ${{matrix.java-version-file }} - name: Verify Java - run: bash __tests__/verify-java.sh "17.0.10" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "17.0.10" "$JAVA_PATH" shell: bash setup-java-version-from-file-major-minor-patch-with-dist: @@ -527,6 +590,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v7 + with: + persist-credentials: false - name: Create .java-version file shell: bash run: echo "openjdk64-17.0.10" > .java-version @@ -543,5 +608,7 @@ jobs: distribution: ${{ matrix.distribution }} java-version-file: ${{matrix.java-version-file }} - name: Verify Java - run: bash __tests__/verify-java.sh "17.0.10" "${{ steps.setup-java.outputs.path }}" + env: + JAVA_PATH: ${{ steps.setup-java.outputs.path }} + run: bash __tests__/verify-java.sh "17.0.10" "$JAVA_PATH" shell: bash diff --git a/.github/workflows/licensed.yml b/.github/workflows/licensed.yml index 37f1560c..b5d009cb 100644 --- a/.github/workflows/licensed.yml +++ b/.github/workflows/licensed.yml @@ -9,6 +9,9 @@ on: - main workflow_dispatch: +permissions: + contents: read + jobs: call-licensed: name: Licensed diff --git a/.github/workflows/publish-immutable-actions.yml b/.github/workflows/publish-immutable-actions.yml index 21d96a8d..3888f9a8 100644 --- a/.github/workflows/publish-immutable-actions.yml +++ b/.github/workflows/publish-immutable-actions.yml @@ -5,6 +5,8 @@ on: types: [released] workflow_dispatch: +permissions: {} + jobs: publish: runs-on: ubuntu-latest @@ -16,6 +18,8 @@ jobs: steps: - name: Checking out uses: actions/checkout@v7 + with: + persist-credentials: false - name: Publish id: publish uses: actions/publish-immutable-action@v0.0.4 diff --git a/.github/workflows/update-config-files.yml b/.github/workflows/update-config-files.yml index 87af5004..bacdc74e 100644 --- a/.github/workflows/update-config-files.yml +++ b/.github/workflows/update-config-files.yml @@ -5,7 +5,12 @@ on: - cron: '0 3 * * 0' workflow_dispatch: +permissions: {} + jobs: call-update-configuration-files: name: Update configuration files + permissions: + contents: write # to push the branch with updated configuration files + pull-requests: write # to open/update the configuration update PR uses: actions/reusable-workflows/.github/workflows/update-config-files.yml@main diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 00000000..11a0f96a --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,48 @@ +name: Security analysis with zizmor + +on: + push: + branches: + - main + - releases/* + paths-ignore: + - '**.md' + pull_request: + paths-ignore: + - '**.md' + workflow_dispatch: + +permissions: {} + +jobs: + zizmor: + name: Analyze workflows with zizmor + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write # to upload SARIF results to code scanning + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Install zizmor + run: pip install zizmor + + - name: Run zizmor + run: zizmor --format sarif .github/workflows/ > zizmor.sarif + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload SARIF results to code scanning + if: always() && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: zizmor.sarif + category: zizmor diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 00000000..38309ec4 --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,11 @@ +# Configuration for zizmor (https://docs.zizmor.sh) +rules: + unpinned-uses: + config: + # First-party GitHub-maintained actions are trusted and referenced by + # major-version tags (the convention used across the actions org). + # Any third-party action must be pinned to a full commit SHA. + policies: + actions/*: ref-pin + github/*: ref-pin + '*': hash-pin From 1d56e31dbb83904d53629e4e0bd2d956e011c1c2 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 23 Jun 2026 13:19:27 -0400 Subject: [PATCH 6/8] dist: Add GraalVM Community distribution support (#1042) * Initial plan * feat: add graalvm community distribution support * build: update bundled dist for graalvm community support * chore: address GraalVM community review feedback * fix: tidy graalvm community validation follow-ups * refactor: simplify GraalVM Community release resolution * refactor: address review feedback on Community resolver * refactor: rename pagination index for clarity * test: fix graalvm installer test formatting --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Bruno Borges --- README.md | 2 + .../distributors/graalvm-installer.test.ts | 128 ++++++++- dist/setup/index.js | 156 +++++++++-- docs/advanced-usage.md | 16 ++ src/distributions/distribution-factory.ts | 8 +- src/distributions/graalvm/installer.ts | 243 +++++++++++++++--- 6 files changed, 495 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 03860b37..b79760e9 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,7 @@ Currently, the following distributions are supported: | `dragonwell` | [Alibaba Dragonwell JDK](https://dragonwell-jdk.io/) | [`dragonwell` license](https://www.aliyun.com/product/dragonwell/) | `sapmachine` | [SAP SapMachine JDK/JRE](https://sapmachine.io/) | [`sapmachine` license](https://github.com/SAP/SapMachine/blob/sapmachine/LICENSE) | `graalvm` | [Oracle GraalVM](https://www.graalvm.org/) | [`graalvm` license](https://www.oracle.com/downloads/licenses/graal-free-license.html) +| `graalvm-community` | [GraalVM Community](https://github.com/graalvm/graalvm-ce-builds/releases) | [`graalvm-community` license](https://github.com/oracle/graal/blob/master/LICENSE) | `jetbrains` | [JetBrains Runtime](https://github.com/JetBrains/JetBrainsRuntime/) | [`jetbrains` license](https://github.com/JetBrains/JetBrainsRuntime/blob/main/LICENSE) | `jdkfile` | Custom JDK Installation | | @@ -120,6 +121,7 @@ Currently, the following distributions are supported: > - AdoptOpenJDK got moved to Eclipse Temurin and won't be updated anymore. It is highly recommended to migrate workflows from `adopt` and `adopt-openj9`, to `temurin` and `semeru` respectively, to keep receiving software and security updates. See more details in the [Good-bye AdoptOpenJDK post](https://blog.adoptopenjdk.net/2021/08/goodbye-adoptopenjdk-hello-adoptium/). > - For Azul Zulu OpenJDK architectures x64 and arm64 are mapped to x86 / arm with proper hw_bitness. > - To comply with the GraalVM Free Terms and Conditions (GFTC) license, it is recommended to use GraalVM JDK 17 version 17.0.12, as this is the only version of GraalVM JDK 17 available under the GFTC license. Additionally, it is encouraged to consider upgrading to GraalVM JDK 21, which offers the latest features and improvements. +> - GraalVM Community is available as `distribution: 'graalvm-community'` for stable JDK 17 and later releases published on GitHub. **NOTE:** Oracle JDK 17 licensing varies by patch level. As shown on the [JDK 17 Archive](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) (versions up to 17.0.12 are under the [NFTC](https://www.oracle.com/downloads/licenses/no-fee-license.html) license) and the [JDK 17.0.13+ Archive](https://www.oracle.com/java/technologies/javase/jdk17-0-13-later-archive-downloads.html) (versions 17.0.13 and later are under the [OTN](https://www.oracle.com/downloads/licenses/javase-license1.html) license). To stay on the free NFTC license, use `distribution: 'oracle'` with `java-version: '17.0.12'` (or earlier) instead of the floating `'17'`. Alternatively, upgrade to Oracle JDK 21+, which remains under the NFTC license. diff --git a/__tests__/distributors/graalvm-installer.test.ts b/__tests__/distributors/graalvm-installer.test.ts index 23f90b88..155e3d9e 100644 --- a/__tests__/distributors/graalvm-installer.test.ts +++ b/__tests__/distributors/graalvm-installer.test.ts @@ -3,7 +3,11 @@ import * as tc from '@actions/tool-cache'; import * as http from '@actions/http-client'; import fs from 'fs'; import path from 'path'; -import {GraalVMDistribution} from '../../src/distributions/graalvm/installer'; +import { + GraalVMCommunityDistribution, + GraalVMDistribution +} from '../../src/distributions/graalvm/installer'; +import {getJavaDistribution} from '../../src/distributions/distribution-factory'; import {JavaInstallerOptions} from '../../src/distributions/base-models'; import * as util from '../../src/util'; @@ -41,6 +45,7 @@ beforeAll(() => { describe('GraalVMDistribution', () => { let distribution: GraalVMDistribution; + let communityDistribution: GraalVMCommunityDistribution; let mockHttpClient: jest.Mocked; let spyCoreError: jest.SpyInstance; @@ -55,9 +60,11 @@ describe('GraalVMDistribution', () => { jest.clearAllMocks(); distribution = new GraalVMDistribution(defaultOptions); + communityDistribution = new GraalVMCommunityDistribution(defaultOptions); mockHttpClient = new http.HttpClient() as jest.Mocked; (distribution as any).http = mockHttpClient; + (communityDistribution as any).http = mockHttpClient; (util.getDownloadArchiveExtension as jest.Mock).mockReturnValue('tar.gz'); @@ -242,6 +249,23 @@ describe('GraalVMDistribution', () => { path: '/cached/java/path' }); }); + + it('should use a dedicated toolcache folder for GraalVM Community', async () => { + const result = await (communityDistribution as any).downloadTool( + javaRelease + ); + + expect(tc.cacheDir).toHaveBeenCalledWith( + path.join('/tmp/extracted', 'graalvm-jdk-17.0.5'), + 'Java_GraalVM_Community_jdk', + '17.0.5', + 'x64' + ); + expect(result).toEqual({ + version: '17.0.5', + path: '/cached/java/path' + }); + }); }); describe('findPackageForDownload', () => { @@ -948,5 +972,107 @@ describe('GraalVMDistribution', () => { configurable: true }); }); + + describe('GraalVMCommunityDistribution', () => { + beforeEach(() => { + jest + .spyOn(communityDistribution, 'getPlatform') + .mockReturnValue('linux'); + }); + + it('should resolve an exact GraalVM Community version from GitHub releases', async () => { + mockHttpClient.getJson.mockResolvedValue({ + result: [ + { + draft: false, + prerelease: false, + assets: [ + { + name: 'graalvm-community-jdk-21.0.2_linux-x64_bin.tar.gz', + browser_download_url: + 'https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-21.0.2/graalvm-community-jdk-21.0.2_linux-x64_bin.tar.gz' + } + ] + } + ], + statusCode: 200, + headers: {} + }); + + const result = await ( + communityDistribution as any + ).findPackageForDownload('21.0.2'); + + expect(result).toEqual({ + url: 'https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-21.0.2/graalvm-community-jdk-21.0.2_linux-x64_bin.tar.gz', + version: '21.0.2' + }); + }); + + it('should resolve the latest GraalVM Community release for a major version', async () => { + mockHttpClient.getJson.mockResolvedValue({ + result: [ + { + draft: false, + prerelease: false, + assets: [ + { + name: 'graalvm-community-jdk-21.0.1_linux-x64_bin.tar.gz', + browser_download_url: + 'https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-21.0.1/graalvm-community-jdk-21.0.1_linux-x64_bin.tar.gz' + } + ] + }, + { + draft: false, + prerelease: false, + assets: [ + { + name: 'graalvm-community-jdk-21.0.2_linux-x64_bin.tar.gz', + browser_download_url: + 'https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-21.0.2/graalvm-community-jdk-21.0.2_linux-x64_bin.tar.gz' + } + ] + } + ], + statusCode: 200, + headers: {} + }); + + const result = await ( + communityDistribution as any + ).findPackageForDownload('21'); + + expect(result).toEqual({ + url: 'https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-21.0.2/graalvm-community-jdk-21.0.2_linux-x64_bin.tar.gz', + version: '21.0.2' + }); + }); + + it('should reject GraalVM Community early access requests', async () => { + (communityDistribution as any).stable = false; + + await expect( + (communityDistribution as any).findPackageForDownload('23') + ).rejects.toThrow( + 'GraalVM Community does not provide early access builds' + ); + }); + }); + }); +}); + +describe('distribution factory', () => { + const defaultOptions: JavaInstallerOptions = { + version: '17', + architecture: 'x64', + packageType: 'jdk', + checkLatest: false + }; + + it('should map graalvm-community to the community installer', () => { + const community = getJavaDistribution('graalvm-community', defaultOptions); + + expect(community).toBeInstanceOf(GraalVMCommunityDistribution); }); }); diff --git a/dist/setup/index.js b/dist/setup/index.js index 33056027..007b4849 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -78771,6 +78771,7 @@ var JavaDistribution; JavaDistribution["Dragonwell"] = "dragonwell"; JavaDistribution["SapMachine"] = "sapmachine"; JavaDistribution["GraalVM"] = "graalvm"; + JavaDistribution["GraalVMCommunity"] = "graalvm-community"; JavaDistribution["JetBrains"] = "jetbrains"; })(JavaDistribution || (JavaDistribution = {})); function getJavaDistribution(distributionName, installerOptions, jdkFile) { @@ -78802,6 +78803,8 @@ function getJavaDistribution(distributionName, installerOptions, jdkFile) { return new installer_11.SapMachineDistribution(installerOptions); case JavaDistribution.GraalVM: return new installer_12.GraalVMDistribution(installerOptions); + case JavaDistribution.GraalVMCommunity: + return new installer_12.GraalVMCommunityDistribution(installerOptions); case JavaDistribution.JetBrains: return new installer_13.JetBrainsDistribution(installerOptions); default: @@ -79069,23 +79072,29 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.GraalVMDistribution = void 0; +exports.GraalVMCommunityDistribution = exports.GraalVMDistribution = void 0; const core = __importStar(__nccwpck_require__(37484)); const tc = __importStar(__nccwpck_require__(33472)); const fs_1 = __importDefault(__nccwpck_require__(79896)); const path_1 = __importDefault(__nccwpck_require__(16928)); +const semver_1 = __importDefault(__nccwpck_require__(62088)); const base_installer_1 = __nccwpck_require__(79935); const http_client_1 = __nccwpck_require__(54844); const util_1 = __nccwpck_require__(54527); const GRAALVM_DL_BASE = 'https://download.oracle.com/graalvm'; const GRAALVM_DOWNLOAD_URL = 'https://www.graalvm.org/downloads/'; +const GRAALVM_COMMUNITY_RELEASES_URL = 'https://api.github.com/repos/graalvm/graalvm-ce-builds/releases?per_page=100'; +const GRAALVM_COMMUNITY_RELEASES_PAGE_ORIGIN = 'https://api.github.com'; +const GRAALVM_COMMUNITY_DOWNLOAD_URL = 'https://github.com/graalvm/graalvm-ce-builds/releases'; +const GRAALVM_COMMUNITY_ASSET_PREFIX = 'graalvm-community-jdk-'; +const GRAALVM_COMMUNITY_VERSION_PATTERN = /^\d+(?:\.\d+)*$/; const IS_WINDOWS = process.platform === 'win32'; const GRAALVM_PLATFORM = IS_WINDOWS ? 'windows' : process.platform; const GRAALVM_MIN_VERSION = 17; const SUPPORTED_ARCHITECTURES = ['x64', 'aarch64']; class GraalVMDistribution extends base_installer_1.JavaBase { - constructor(installerOptions) { - super('GraalVM', installerOptions); + constructor(installerOptions, distributionName = 'GraalVM') { + super(distributionName, installerOptions); } downloadTool(javaRelease) { return __awaiter(this, void 0, void 0, function* () { @@ -79119,36 +79128,50 @@ class GraalVMDistribution extends base_installer_1.JavaBase { } findPackageForDownload(range) { return __awaiter(this, void 0, void 0, function* () { - // Add input validation - if (!range || typeof range !== 'string') { - throw new Error('Version range is required and must be a string'); - } - const arch = this.distributionArchitecture(); - if (!SUPPORTED_ARCHITECTURES.includes(arch)) { - throw new Error(`Unsupported architecture: ${this.architecture}. Supported architectures are: ${SUPPORTED_ARCHITECTURES.join(', ')}`); - } + this.validateVersionRange(range); + const arch = this.getSupportedArchitecture(); if (!this.stable) { return this.findEABuildDownloadUrl(`${range}-ea`); } - if (this.packageType !== 'jdk') { - throw new Error('GraalVM provides only the `jdk` package type'); - } - const platform = this.getPlatform(); - const extension = (0, util_1.getDownloadArchiveExtension)(); - const major = range.includes('.') ? range.split('.')[0] : range; - const majorVersion = parseInt(major); - if (isNaN(majorVersion)) { - throw new Error(`Invalid version format: ${range}`); - } - if (majorVersion < GRAALVM_MIN_VERSION) { - throw new Error(`GraalVM is only supported for JDK ${GRAALVM_MIN_VERSION} and later. Requested version: ${major}`); - } + const { platform, extension, major } = this.validateStableBuildRequest(range); const fileUrl = this.constructFileUrl(range, major, platform, arch, extension); const response = yield this.http.head(fileUrl); this.handleHttpResponse(response, range); return { url: fileUrl, version: range }; }); } + validateVersionRange(range) { + if (!range || typeof range !== 'string') { + throw new Error('Version range is required and must be a string'); + } + } + getSupportedArchitecture() { + const arch = this.distributionArchitecture(); + if (!SUPPORTED_ARCHITECTURES.includes(arch)) { + throw new Error(`Unsupported architecture: ${this.architecture}. Supported architectures are: ${SUPPORTED_ARCHITECTURES.join(', ')}`); + } + return arch; + } + validateStableBuildRequest(range) { + if (this.packageType !== 'jdk') { + throw new Error(`${this.distribution} provides only the \`jdk\` package type`); + } + const platform = this.getPlatform(); + const extension = (0, util_1.getDownloadArchiveExtension)(); + const major = range.includes('.') ? range.split('.')[0] : range; + const majorVersion = parseInt(major); + if (isNaN(majorVersion)) { + throw new Error(`Invalid version format: ${range}`); + } + if (majorVersion < GRAALVM_MIN_VERSION) { + throw new Error(`${this.distribution} is only supported for JDK ${GRAALVM_MIN_VERSION} and later. Requested version: ${major}`); + } + return { + platform, + major, + extension + }; + } constructFileUrl(range, major, platform, arch, extension) { return range.includes('.') ? `${GRAALVM_DL_BASE}/${major}/archive/graalvm-jdk-${range}_${platform}-${arch}_bin.${extension}` @@ -79239,6 +79262,91 @@ class GraalVMDistribution extends base_installer_1.JavaBase { } } exports.GraalVMDistribution = GraalVMDistribution; +class GraalVMCommunityDistribution extends GraalVMDistribution { + constructor(installerOptions) { + super(installerOptions, 'GraalVM Community'); + } + get toolcacheFolderName() { + return `Java_GraalVM_Community_${this.packageType}`; + } + findPackageForDownload(range) { + return __awaiter(this, void 0, void 0, function* () { + this.validateVersionRange(range); + if (!this.stable) { + throw new Error('GraalVM Community does not provide early access builds'); + } + const arch = this.getSupportedArchitecture(); + const { platform, extension } = this.validateStableBuildRequest(range); + // GraalVM Community asset names embed the platform, architecture and + // archive type, e.g. `graalvm-community-jdk-21.0.2_linux-x64_bin.tar.gz`. + const assetSuffix = `_${platform}-${arch}_bin.${extension}`; + const availableVersions = yield this.getAvailableVersions(assetSuffix); + const satisfiedVersion = availableVersions + .filter(item => (0, util_1.isVersionSatisfies)(range, item.version)) + .sort((a, b) => -semver_1.default.compareBuild(a.version, b.version))[0]; + if (!satisfiedVersion) { + const error = this.createVersionNotFoundError(range, availableVersions.map(item => item.version), `Platform: ${platform}`); + error.message += `\nPlease check if this version is available at ${GRAALVM_COMMUNITY_DOWNLOAD_URL}.`; + throw error; + } + return satisfiedVersion; + }); + } + getAvailableVersions(assetSuffix) { + var _a; + return __awaiter(this, void 0, void 0, function* () { + const headers = (0, util_1.getGitHubHttpHeaders)(); + const versions = new Map(); + let releasesUrl = GRAALVM_COMMUNITY_RELEASES_URL; + for (let pageIndex = 0; releasesUrl && pageIndex < util_1.MAX_PAGINATION_PAGES; pageIndex++) { + const response = yield this.http.getJson(releasesUrl, headers); + const releases = Array.isArray(response.result) ? response.result : []; + if (releases.length === 0) { + break; + } + for (const release of releases) { + if (release.draft || release.prerelease) { + continue; + } + for (const asset of (_a = release.assets) !== null && _a !== void 0 ? _a : []) { + const version = this.extractAssetVersion(asset.name, assetSuffix); + if (version) { + versions.set(version, { + version, + url: asset.browser_download_url + }); + } + } + } + releasesUrl = this.getNextReleasesUrl(response.headers); + } + return [...versions.values()]; + }); + } + // Returns the GraalVM JDK version encoded in a release asset name when it + // matches the requested platform/architecture/archive suffix, otherwise null. + extractAssetVersion(assetName, assetSuffix) { + if (!assetName.startsWith(GRAALVM_COMMUNITY_ASSET_PREFIX) || + !assetName.endsWith(assetSuffix)) { + return null; + } + const rawVersion = assetName.slice(GRAALVM_COMMUNITY_ASSET_PREFIX.length, -assetSuffix.length); + if (!GRAALVM_COMMUNITY_VERSION_PATTERN.test(rawVersion)) { + return null; + } + return (0, util_1.convertVersionToSemver)(rawVersion); + } + getNextReleasesUrl(headers) { + const nextUrl = (0, util_1.getNextPageUrlFromLinkHeader)(headers); + if (nextUrl && + !(0, util_1.validatePaginationUrl)(nextUrl, GRAALVM_COMMUNITY_RELEASES_PAGE_ORIGIN)) { + core.warning(`Ignoring pagination link with unexpected origin: ${nextUrl}`); + return null; + } + return nextUrl; + } +} +exports.GraalVMCommunityDistribution = GraalVMCommunityDistribution; /***/ }), diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index f4838b34..d3a8e312 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -10,6 +10,7 @@ - [Alibaba Dragonwell](#Alibaba-Dragonwell) - [SapMachine](#SapMachine) - [GraalVM](#GraalVM) + - [GraalVM Community](#GraalVM-Community) - [JetBrains](#JetBrains) - [Installing custom Java package type](#Installing-custom-Java-package-type) - [JavaFX Maven project](#JavaFX-Maven-project) @@ -174,6 +175,21 @@ steps: native-image --version ``` +### GraalVM Community +**NOTE:** GraalVM Community is available for stable JDK 17 and later releases. + +```yaml +steps: +- uses: actions/checkout@v6 +- uses: actions/setup-java@v5 + with: + distribution: 'graalvm-community' + java-version: '21' +- run: | + java -cp java HelloWorldApp + native-image -cp java HelloWorldApp +``` + ### JetBrains **NOTE:** JetBrains is only available for LTS versions on 11 or later (11, 17, 21, etc.). diff --git a/src/distributions/distribution-factory.ts b/src/distributions/distribution-factory.ts index 9cd459e6..0ff6597e 100644 --- a/src/distributions/distribution-factory.ts +++ b/src/distributions/distribution-factory.ts @@ -11,7 +11,10 @@ import {CorrettoDistribution} from './corretto/installer'; import {OracleDistribution} from './oracle/installer'; import {DragonwellDistribution} from './dragonwell/installer'; import {SapMachineDistribution} from './sapmachine/installer'; -import {GraalVMDistribution} from './graalvm/installer'; +import { + GraalVMCommunityDistribution, + GraalVMDistribution +} from './graalvm/installer'; import {JetBrainsDistribution} from './jetbrains/installer'; enum JavaDistribution { @@ -29,6 +32,7 @@ enum JavaDistribution { Dragonwell = 'dragonwell', SapMachine = 'sapmachine', GraalVM = 'graalvm', + GraalVMCommunity = 'graalvm-community', JetBrains = 'jetbrains' } @@ -74,6 +78,8 @@ export function getJavaDistribution( return new SapMachineDistribution(installerOptions); case JavaDistribution.GraalVM: return new GraalVMDistribution(installerOptions); + case JavaDistribution.GraalVMCommunity: + return new GraalVMCommunityDistribution(installerOptions); case JavaDistribution.JetBrains: return new JetBrainsDistribution(installerOptions); default: diff --git a/src/distributions/graalvm/installer.ts b/src/distributions/graalvm/installer.ts index fea3b8f1..2b2442f8 100644 --- a/src/distributions/graalvm/installer.ts +++ b/src/distributions/graalvm/installer.ts @@ -2,6 +2,7 @@ import * as core from '@actions/core'; import * as tc from '@actions/tool-cache'; import fs from 'fs'; import path from 'path'; +import semver from 'semver'; import {JavaBase} from '../base-installer'; import {HttpCodes} from '@actions/http-client'; import {GraalVMEAVersion} from './models'; @@ -11,14 +12,26 @@ import { JavaInstallerResults } from '../base-models'; import { + convertVersionToSemver, extractJdkFile, getDownloadArchiveExtension, getGitHubHttpHeaders, - renameWinArchive + getNextPageUrlFromLinkHeader, + isVersionSatisfies, + MAX_PAGINATION_PAGES, + renameWinArchive, + validatePaginationUrl } from '../../util'; const GRAALVM_DL_BASE = 'https://download.oracle.com/graalvm'; const GRAALVM_DOWNLOAD_URL = 'https://www.graalvm.org/downloads/'; +const GRAALVM_COMMUNITY_RELEASES_URL = + 'https://api.github.com/repos/graalvm/graalvm-ce-builds/releases?per_page=100'; +const GRAALVM_COMMUNITY_RELEASES_PAGE_ORIGIN = 'https://api.github.com'; +const GRAALVM_COMMUNITY_DOWNLOAD_URL = + 'https://github.com/graalvm/graalvm-ce-builds/releases'; +const GRAALVM_COMMUNITY_ASSET_PREFIX = 'graalvm-community-jdk-'; +const GRAALVM_COMMUNITY_VERSION_PATTERN = /^\d+(?:\.\d+)*$/; const IS_WINDOWS = process.platform === 'win32'; const GRAALVM_PLATFORM = IS_WINDOWS ? 'windows' : process.platform; const GRAALVM_MIN_VERSION = 17; @@ -26,9 +39,23 @@ const SUPPORTED_ARCHITECTURES = ['x64', 'aarch64'] as const; type SupportedArchitecture = (typeof SUPPORTED_ARCHITECTURES)[number]; type OsVersions = 'linux' | 'macos' | 'windows'; +interface GraalVMCommunityAsset { + name: string; + browser_download_url: string; +} + +interface GraalVMCommunityRelease { + draft: boolean; + prerelease: boolean; + assets: GraalVMCommunityAsset[]; +} + export class GraalVMDistribution extends JavaBase { - constructor(installerOptions: JavaInstallerOptions) { - super('GraalVM', installerOptions); + constructor( + installerOptions: JavaInstallerOptions, + distributionName = 'GraalVM' + ) { + super(distributionName, installerOptions); } protected async downloadTool( @@ -85,40 +112,14 @@ export class GraalVMDistribution extends JavaBase { protected async findPackageForDownload( range: string ): Promise { - // Add input validation - if (!range || typeof range !== 'string') { - throw new Error('Version range is required and must be a string'); - } - - const arch = this.distributionArchitecture(); - if (!SUPPORTED_ARCHITECTURES.includes(arch as SupportedArchitecture)) { - throw new Error( - `Unsupported architecture: ${this.architecture}. Supported architectures are: ${SUPPORTED_ARCHITECTURES.join(', ')}` - ); - } + this.validateVersionRange(range); + const arch = this.getSupportedArchitecture(); if (!this.stable) { return this.findEABuildDownloadUrl(`${range}-ea`); } - if (this.packageType !== 'jdk') { - throw new Error('GraalVM provides only the `jdk` package type'); - } - - const platform = this.getPlatform(); - const extension = getDownloadArchiveExtension(); - const major = range.includes('.') ? range.split('.')[0] : range; - const majorVersion = parseInt(major); - - if (isNaN(majorVersion)) { - throw new Error(`Invalid version format: ${range}`); - } - - if (majorVersion < GRAALVM_MIN_VERSION) { - throw new Error( - `GraalVM is only supported for JDK ${GRAALVM_MIN_VERSION} and later. Requested version: ${major}` - ); - } + const {platform, extension, major} = this.validateStableBuildRequest(range); const fileUrl = this.constructFileUrl( range, @@ -134,6 +135,56 @@ export class GraalVMDistribution extends JavaBase { return {url: fileUrl, version: range}; } + protected validateVersionRange(range: string): void { + if (!range || typeof range !== 'string') { + throw new Error('Version range is required and must be a string'); + } + } + + protected getSupportedArchitecture(): SupportedArchitecture { + const arch = this.distributionArchitecture(); + if (!SUPPORTED_ARCHITECTURES.includes(arch as SupportedArchitecture)) { + throw new Error( + `Unsupported architecture: ${this.architecture}. Supported architectures are: ${SUPPORTED_ARCHITECTURES.join(', ')}` + ); + } + + return arch as SupportedArchitecture; + } + + protected validateStableBuildRequest(range: string): { + platform: OsVersions; + extension: string; + major: string; + } { + if (this.packageType !== 'jdk') { + throw new Error( + `${this.distribution} provides only the \`jdk\` package type` + ); + } + + const platform = this.getPlatform(); + const extension = getDownloadArchiveExtension(); + const major = range.includes('.') ? range.split('.')[0] : range; + const majorVersion = parseInt(major); + + if (isNaN(majorVersion)) { + throw new Error(`Invalid version format: ${range}`); + } + + if (majorVersion < GRAALVM_MIN_VERSION) { + throw new Error( + `${this.distribution} is only supported for JDK ${GRAALVM_MIN_VERSION} and later. Requested version: ${major}` + ); + } + + return { + platform, + major, + extension + }; + } + private constructFileUrl( range: string, major: string, @@ -280,3 +331,131 @@ export class GraalVMDistribution extends JavaBase { return result; } } + +export class GraalVMCommunityDistribution extends GraalVMDistribution { + constructor(installerOptions: JavaInstallerOptions) { + super(installerOptions, 'GraalVM Community'); + } + + protected get toolcacheFolderName(): string { + return `Java_GraalVM_Community_${this.packageType}`; + } + + protected async findPackageForDownload( + range: string + ): Promise { + this.validateVersionRange(range); + + if (!this.stable) { + throw new Error('GraalVM Community does not provide early access builds'); + } + + const arch = this.getSupportedArchitecture(); + const {platform, extension} = this.validateStableBuildRequest(range); + // GraalVM Community asset names embed the platform, architecture and + // archive type, e.g. `graalvm-community-jdk-21.0.2_linux-x64_bin.tar.gz`. + const assetSuffix = `_${platform}-${arch}_bin.${extension}`; + const availableVersions = await this.getAvailableVersions(assetSuffix); + + const satisfiedVersion = availableVersions + .filter(item => isVersionSatisfies(range, item.version)) + .sort((a, b) => -semver.compareBuild(a.version, b.version))[0]; + + if (!satisfiedVersion) { + const error = this.createVersionNotFoundError( + range, + availableVersions.map(item => item.version), + `Platform: ${platform}` + ); + error.message += `\nPlease check if this version is available at ${GRAALVM_COMMUNITY_DOWNLOAD_URL}.`; + throw error; + } + + return satisfiedVersion; + } + + private async getAvailableVersions( + assetSuffix: string + ): Promise { + const headers = getGitHubHttpHeaders(); + const versions = new Map(); + let releasesUrl: string | null = GRAALVM_COMMUNITY_RELEASES_URL; + + for ( + let pageIndex = 0; + releasesUrl && pageIndex < MAX_PAGINATION_PAGES; + pageIndex++ + ) { + const response = await this.http.getJson( + releasesUrl, + headers + ); + + const releases = Array.isArray(response.result) ? response.result : []; + if (releases.length === 0) { + break; + } + + for (const release of releases) { + if (release.draft || release.prerelease) { + continue; + } + + for (const asset of release.assets ?? []) { + const version = this.extractAssetVersion(asset.name, assetSuffix); + if (version) { + versions.set(version, { + version, + url: asset.browser_download_url + }); + } + } + } + + releasesUrl = this.getNextReleasesUrl(response.headers); + } + + return [...versions.values()]; + } + + // Returns the GraalVM JDK version encoded in a release asset name when it + // matches the requested platform/architecture/archive suffix, otherwise null. + private extractAssetVersion( + assetName: string, + assetSuffix: string + ): string | null { + if ( + !assetName.startsWith(GRAALVM_COMMUNITY_ASSET_PREFIX) || + !assetName.endsWith(assetSuffix) + ) { + return null; + } + + const rawVersion = assetName.slice( + GRAALVM_COMMUNITY_ASSET_PREFIX.length, + -assetSuffix.length + ); + + if (!GRAALVM_COMMUNITY_VERSION_PATTERN.test(rawVersion)) { + return null; + } + + return convertVersionToSemver(rawVersion); + } + + private getNextReleasesUrl( + headers: Record + ): string | null { + const nextUrl = getNextPageUrlFromLinkHeader(headers); + if ( + nextUrl && + !validatePaginationUrl(nextUrl, GRAALVM_COMMUNITY_RELEASES_PAGE_ORIGIN) + ) { + core.warning( + `Ignoring pagination link with unexpected origin: ${nextUrl}` + ); + return null; + } + return nextUrl; + } +} From fa2c6508d1036292a5efdf642f98d1c695974c72 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Tue, 23 Jun 2026 13:23:45 -0400 Subject: [PATCH 7/8] docs: note jdkfile approach for Early Access / unreleased JDK builds (#1058) * docs: note jdkfile approach for Early Access / unreleased JDK builds Clarify in advanced-usage that the existing 'jdkfile' distribution can be used to install Early Access (EA) or other unreleased JDK builds not provided directly by setup-java, by downloading the archive in a prior step and pointing jdkFile at it. Adds a concrete EA example. Addresses #612. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- docs/advanced-usage.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index d3a8e312..b12a49b9 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -286,6 +286,9 @@ steps: ## Installing Java from local file If your use-case requires a custom distribution or a version that is not provided by setup-java, you can download it manually and setup-java will take care of the installation and caching on the VM: +> [!NOTE] +> This approach also lets you use builds that setup-java does not provide directly, such as **Early Access (EA)** or other unreleased JDK builds (for example, an upcoming feature release or a Loom/Valhalla preview build). Download the desired archive in a prior step and point `jdkFile` at it; setup-java will extract, install, and cache it just like a supported distribution. When targeting multiple architectures, select the correct binary per architecture in your workflow (for example, with a build matrix). + ```yaml steps: - run: | @@ -301,6 +304,23 @@ steps: - run: java --version ``` +For example, to use an **Early Access** build from [jdk.java.net](https://jdk.java.net/), download the archive for your runner OS/architecture and install it via `distribution: 'jdkfile'` (example below assumes Linux x64): + +```yaml +steps: +- run: | + download_url="https://download.java.net/java/early_access/jdk25/36/GPL/openjdk-25-ea+36_linux-x64_bin.tar.gz" + wget -O $RUNNER_TEMP/java_package.tar.gz $download_url +- uses: actions/setup-java@v5 + with: + distribution: 'jdkfile' + jdkFile: ${{ runner.temp }}/java_package.tar.gz + java-version: '25.0.0-ea.36' + architecture: x64 + +- run: java --version +``` + If your use-case requires a custom distribution (in the example, alpine-linux is used) or a version that is not provided by setup-java and you want to always install the latest version during runtime, then you can use the following code to auto-download the latest JDK, determine the semver needed for setup-java, and setup-java will take care of the installation and caching on the VM: ```yaml From 1bcf9fb12cf4aa7d266a90ae39939e61372fe520 Mon Sep 17 00:00:00 2001 From: Bruno Borges Date: Tue, 23 Jun 2026 13:37:44 -0400 Subject: [PATCH 8/8] dist: Address Copilot review suggestions from PR #1042 (GraalVM Community) (#1059) - installer: surface a clear error when the GraalVM Community releases listing is not a JSON array, instead of silently treating an error payload (rate limit, auth failure, etc.) as "no releases" which later surfaced as a misleading "version not found" error. - docs: fix the GraalVM Community advanced-usage example to check the installed binary versions (java/native-image --version) rather than running a non-existent HelloWorldApp classpath that fails when copied. - tests: cover the new non-array release listing error path. Rebuilt dist bundle. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- __tests__/distributors/graalvm-installer.test.ts | 14 ++++++++++++++ dist/setup/index.js | 12 +++++++++++- docs/advanced-usage.md | 4 ++-- src/distributions/graalvm/installer.ts | 15 ++++++++++++++- 4 files changed, 41 insertions(+), 4 deletions(-) diff --git a/__tests__/distributors/graalvm-installer.test.ts b/__tests__/distributors/graalvm-installer.test.ts index 155e3d9e..beefbd13 100644 --- a/__tests__/distributors/graalvm-installer.test.ts +++ b/__tests__/distributors/graalvm-installer.test.ts @@ -1058,6 +1058,20 @@ describe('GraalVMDistribution', () => { 'GraalVM Community does not provide early access builds' ); }); + + it('should surface an error when the releases listing is not an array', async () => { + mockHttpClient.getJson.mockResolvedValue({ + result: {message: 'API rate limit exceeded'}, + statusCode: 403, + headers: {} + }); + + await expect( + (communityDistribution as any).findPackageForDownload('21') + ).rejects.toThrow( + /Unexpected response while listing GraalVM Community releases.*HTTP status code: 403/s + ); + }); }); }); }); diff --git a/dist/setup/index.js b/dist/setup/index.js index 007b4849..bbf320eb 100644 --- a/dist/setup/index.js +++ b/dist/setup/index.js @@ -79300,7 +79300,17 @@ class GraalVMCommunityDistribution extends GraalVMDistribution { let releasesUrl = GRAALVM_COMMUNITY_RELEASES_URL; for (let pageIndex = 0; releasesUrl && pageIndex < util_1.MAX_PAGINATION_PAGES; pageIndex++) { const response = yield this.http.getJson(releasesUrl, headers); - const releases = Array.isArray(response.result) ? response.result : []; + // A successful GitHub releases listing is always a JSON array (possibly + // empty). Anything else indicates an unexpected/error payload (rate + // limiting, auth failure, etc.) that must be surfaced instead of being + // silently treated as "no releases", which would later look like a + // misleading "version not found" error. + if (!Array.isArray(response.result)) { + throw new Error(`Unexpected response while listing GraalVM Community releases from ${releasesUrl} ` + + `(HTTP status code: ${response.statusCode}). Expected a JSON array of releases. ` + + `Please check if the service is available at ${GRAALVM_COMMUNITY_DOWNLOAD_URL}.`); + } + const releases = response.result; if (releases.length === 0) { break; } diff --git a/docs/advanced-usage.md b/docs/advanced-usage.md index b12a49b9..58301be9 100644 --- a/docs/advanced-usage.md +++ b/docs/advanced-usage.md @@ -186,8 +186,8 @@ steps: distribution: 'graalvm-community' java-version: '21' - run: | - java -cp java HelloWorldApp - native-image -cp java HelloWorldApp + java --version + native-image --version ``` ### JetBrains diff --git a/src/distributions/graalvm/installer.ts b/src/distributions/graalvm/installer.ts index 2b2442f8..861cf12a 100644 --- a/src/distributions/graalvm/installer.ts +++ b/src/distributions/graalvm/installer.ts @@ -391,7 +391,20 @@ export class GraalVMCommunityDistribution extends GraalVMDistribution { headers ); - const releases = Array.isArray(response.result) ? response.result : []; + // A successful GitHub releases listing is always a JSON array (possibly + // empty). Anything else indicates an unexpected/error payload (rate + // limiting, auth failure, etc.) that must be surfaced instead of being + // silently treated as "no releases", which would later look like a + // misleading "version not found" error. + if (!Array.isArray(response.result)) { + throw new Error( + `Unexpected response while listing GraalVM Community releases from ${releasesUrl} ` + + `(HTTP status code: ${response.statusCode}). Expected a JSON array of releases. ` + + `Please check if the service is available at ${GRAALVM_COMMUNITY_DOWNLOAD_URL}.` + ); + } + + const releases = response.result; if (releases.length === 0) { break; }