diff --git a/docs/changes.md b/docs/changes.md index d4428a2..215b3e1 100644 --- a/docs/changes.md +++ b/docs/changes.md @@ -9,6 +9,8 @@ What changed in each release, as a user notices it. GitHub's release pages list - 13 more distributions, the rest of what Foojay lists: AdoptOpenJDK (`adpt`, `adptj9`), Eliya (`eliya`), OJDKBuild (`ojdkbld`), OpenLogic (`opnlgc`), Red Hat (`rdht`), Semeru certified (`semcrt`), Gluon GraalVM (`gln`) and GraalVM CE for JDK 8 to 19 (`grl8`, `grl11`, `grl16`, `grl17`, `grl19`). Red Hat installs from tar.xz, and OJDKBuild on Linux, OpenLogic on macOS and Gluon GraalVM before 22.1 from zips. A `.sdkmanrc` takes SDKMAN's old identifiers for them, such as `11.0.10.j9-adpt` and `22.3.3.r17-grl`. See [distributions.md](distributions.md). - Mandrel and Liberica NIK install by the GraalVM version they carry again: `java@23.1.12-mandrel` and `java@23.1.8-nik` work, `vfox search java mandrel` lists every release, and `java@23-mandrel` installs the newest 23.x, `23.1.12.1-mandrel`, which runs Java 21. In October 2026 Foojay moved that version from `java_version` to `distribution_version`, and since then release 0.6.2 fails on such versions with `No JDK found`, lists one Mandrel per Java major and installs Mandrel 23.1.8 for `java@21-mandrel`. Installs it made since then keep names after the Java version (`21-mandrel`, `21.0.12.1+2-nik`), and vfox still finds them by those names; uninstall one to get what the spec means now. See [distributions.md](distributions.md#behavior-of-mandrel-and-liberica-nik) ([#106](https://github.com/version-fox/vfox-java/issues/106)). - The notes printed by `vfox add java`, `vfox update java` and `vfox info java` are a link to the README, which lists every short name and shows how to search a distribution. Release 0.6.2 gives five short names there but not how to list their versions, while `vfox search java` lists OpenJDK only ([vfox#390](https://github.com/version-fox/vfox/issues/390)). +- Oracle GraalVM and GraalVM Community go by GraalVM's own version, which Foojay has given as `distribution_version` since October 2026. Release 0.6.2 reads `java_version`, which now holds the JDK's version. So it installs the feature release 25.4.4.1.1 for `java@25.0.4.1-graal`, and for `java@25-graal` whenever a feature release is out on the JDK of the newest 25.0 build, and `vfox search java graal` lists no feature release. Now `java@25-graal` installs the newest build of the 25.0 line (`25.0.5-graal` on 2026-10-20), `java@25.0.4.1-graal` installs GraalVM 25.0.4.1, and the feature release is `java@25.4.4.1.1-graal`. `java@25-graalce` installs `25.4.4.1.1-graalce` from Oracle's GraalVM Download Service, where 0.6.2 installed the same file from GitHub as `25.4.4.1-graalce` ([#105](https://github.com/version-fox/vfox-java/issues/105)). +- A 25.0 name that release 0.6.2 installed after Foojay's change can hold a feature release: `25.0.4.1-graal` is GraalVM 25.4.4.1.1, `25.0.4-graal` is 25.2.4 and `25.0.3-graal` is 25.1.3, and the same goes for these names with `-graalce`. vfox does not install a name it already has, so if the GraalVM version that `java -version` prints differs from the name, `vfox uninstall` it and install it again. `25.0.4.1-graalce`, `25.0.4-graalce` and `25.0.3-graalce` no longer install at all, as GraalVM Community's 25.0 line ends with `25.0.2`. ## 0.6.2 (2026-10-18) diff --git a/docs/development.md b/docs/development.md index b9578a6..5159838 100644 --- a/docs/development.md +++ b/docs/development.md @@ -44,7 +44,7 @@ A few contracts that cost time to find: ## Names -Installed names are `[-][-fx]`, with no short name for OpenJDK and Foojay's `distribution_version` in place of `java_version` for Mandrel and Liberica NIK ([versions.md](versions.md#installed-names)). `lib/distribution_version.lua` reads a spec into a version, a distribution and the JavaFX flag, in either order (`21-tem`, `tem-21`). `lib/installed_version.lua` turns a version the user typed into an installed name: the name itself first. A bare major then takes the newest installed release of it, and any other version takes the release itself or its newest build. `PreUse` stops there. `ParseLegacyFile` goes one step further and falls back to the newest installed release of the major. +Installed names are `[-][-fx]`, with no short name for OpenJDK, Foojay's `distribution_version` in place of `java_version` for Mandrel and Liberica NIK, and GraalVM's own version for `graal` and `graalce` ([versions.md](versions.md#installed-names)). `lib/distribution_version.lua` reads a spec into a version, a distribution and the JavaFX flag, in either order (`21-tem`, `tem-21`). `lib/installed_version.lua` turns a version the user typed into an installed name: the name itself first. A bare major then takes the newest installed release of it, and any other version takes the release itself or its newest build. `PreUse` stops there. `ParseLegacyFile` goes one step further and falls back to the newest installed release of the major. SDKMAN spells some things differently (`jbr`, `21.0.12.fx-zulu`, `21.0.12-fx+1.1-librca`, `28.0.0+ea.19-open`), and `plugin_spelling` in `hooks/parse_legacy_file.lua` rewrites them into the plugin's before parsing. For the distributions SDKMAN no longer lists, `sdkman_flavours` there turns the virtual machine or JDK in SDKMAN's version into the short name (`22.3.3.r17-grl` is `22.3.3-grl17`). `installed_version.unpad` drops the zeros SDKMAN pads releases with (`27.0.0+36` is `27+36`). Where the short names differ from SDKMAN's is in [distributions.md](distributions.md#short-names-and-sdkman). @@ -55,9 +55,10 @@ SDKMAN spells some things differently (`jbr`, `21.0.12.fx-zulu`, `21.0.12-fx+1.1 - Windows asks for `zip` and `tar.gz` in one request. JetBrains Runtime ships Windows zips for its newer builds only. `PreInstall` takes the zip when a version has both. - Elsewhere `other_archive_type` in `lib/foojay.lua` adds one archive type for the distributions that ship no tar.gz there for some or all releases: `tar.xz` for Red Hat, `zip` for OJDKBuild, OpenLogic and Gluon GraalVM. vfox unpacks both, and the zips carry their Unix permissions, so `bin/java` stays executable. - `own_numbers` in `lib/foojay.lua` holds Mandrel and Liberica NIK, which carry the GraalVM version they are built from. Foojay gives it as `distribution_version` and the Java version as `java_version`, for Mandrel the major only, so these two are asked for with `distribution_version` and an empty `version`. Foojay matches the numbers given (`23.1.12` lists 23.1.12.1 too), so a longer version keeps only its own release, and a bare major takes the newest itself, as `latest=available` picks an older one for them. The answer is sorted newest first, then by the newest Java for a release that comes for several, then by BellSoft's later build for the same Java, which only the file name carries. `distribution_version` then goes into `java_version`, so the hooks name and list them like the rest. +- Oracle GraalVM and GraalVM Community go by GraalVM's own version, which Foojay has given as `distribution_version` since October 2026, with the JDK's as `java_version`. `graalvm_packages` in `lib/foojay.lua` asks for every package, the question `vfox search` asks, so that installing sees the builds a search lists ([foojay.md](foojay.md#the-versions-of-one-distribution)). It renames each package after GraalVM's version (`graalvm_version`: the newer of `java_version` and `distribution_version`, as GraalVM Community's feature releases are also listed the old way round), sorts them newest first, and keeps those of a bare major, or the release a longer version names, without its zero padding. For `vfox search` it keeps a download once, under the newer of its two names (`25.4.4.1.1`, not `25.4.4.1`). `latest=available` is not used for them, as it misses releases: on 2026-10-20 its answer for 25 on Windows had GraalVM `25.0.4.1` but not `25.0.4.1.1`, both Java 25.0.4.1. Early-access builds still go by `version`. - `fixes` in `lib/foojay.lua` drops packages before the hooks see them: the JREs, installer bundles and RHEL 6 builds that Foojay lists among OJDKBuild's Windows JDKs, and the JDK 11 builds of Gluon GraalVM's 22.x releases, which share their version with the JDK 17 ones. For a bare major `latest=available` answers with one package per archive type, which can be one the fix drops, so for these two the plugin then asks for that release in full. - `Available` lists a name once where Foojay has two records for it. -- Foojay marks GraalVM's monthly feature releases (`25.1.3` and on) as LTS. `is_feature_release` in `lib/distribution_version.lua` recognises them, by the minor number, and `Available` drops the note. It is for `graal` and `graalce` only: Mandrel and Liberica NIK carry GraalVM's older numbers up to 24, whose minor is no feature release (`mandrel` `23.1.12` is LTS), and the JDK's from 25 on. +- Foojay marks GraalVM Community's monthly feature releases (`25.1.3` and on) as LTS where it lists them the old way round, as it did Oracle GraalVM's. `is_feature_release` in `lib/distribution_version.lua` recognises them, by the minor number, and `Available` drops the note. It is for `graal` and `graalce` only: Mandrel and Liberica NIK carry GraalVM's older numbers up to 24, whose minor is no feature release (`mandrel` `23.1.12` is LTS), and the JDK's from 25 on. - For a bare major of Oracle GraalVM (`graal`) only, `PreInstall` skips the feature releases, so the 25.0 line is installed. A major that has nothing but feature releases keeps them. GraalVM Community is not filtered, because its feature releases are what it still ships. - The CPU names are `arm64` to `aarch64`, and on Windows `amd64` to `x64`. `darwin` becomes `macos`. On Linux `ldd --version` decides between glibc and musl. @@ -89,7 +90,7 @@ vfox plugin run . ParseLegacyFile --input '{"filename":".sdkmanrc","filepath":"/ 1. Find its id in Foojay ([foojay.md](foojay.md)), and check that it has packages for the platforms you care about. 2. Add `[""] = ""` to `short_name` in `lib/distribution_version.lua`. Use SDKMAN's identifier when SDKMAN has or had the distribution. Otherwise make one the way SDKMAN does: keep a short vendor name whole, and cut a longer one down, mostly by dropping vowels (`opnlgc`). If SDKMAN spells it differently, map it in `plugin_spelling` in `hooks/parse_legacy_file.lua`, and add the difference to [distributions.md](distributions.md#short-names-and-sdkman). -3. If its version numbers or its LTS marks need special care, as GraalVM's do, handle that in `hooks/available.lua` and `hooks/pre_install.lua`. If it ships no tar.gz outside Windows, add its archive type to `other_archive_type` in `lib/foojay.lua`, and if Foojay lists packages of it that do not install as they are, add a fix to `fixes` there. +3. If its version numbers need special care, as those of GraalVM and Mandrel do, handle them where `lib/foojay.lua` reads Foojay's answer (`graalvm_packages`, `own_numbers`), and its LTS marks or the pick for a bare major in `hooks/available.lua` and `hooks/pre_install.lua`. If it ships no tar.gz outside Windows, add its archive type to `other_archive_type` in `lib/foojay.lua`, and if Foojay lists packages of it that do not install as they are, add a fix to `fixes` there. 4. Add a case to `tests/pre_install_test.lua`. 5. Update the tables in `README.md`, [distributions.md](distributions.md) and [platforms.md](platforms.md), and their translations in `README_CN.md` and `docs/zh-CN`. 6. For a new code path, also add it to the e2e matrix ([e2e.md](e2e.md)). Distributions outside the matrix still run every check except the `java.vendor` assertion and the asdf-style `PreUse` spelling. One that only brings a name goes into `nameOnly` in `tests/e2e/common/common.go` instead, with the runner platforms Foojay has it for. diff --git a/docs/distributions.md b/docs/distributions.md index 32f0f7e..2479434 100644 --- a/docs/distributions.md +++ b/docs/distributions.md @@ -130,7 +130,7 @@ IBM Semeru Runtime Certified Edition: Semeru's OpenJ9 runtimes, certified agains ### Oracle GraalVM (`graal`) -Oracle's distribution of GraalVM, built on Oracle JDK. Releases carry GraalVM's numbers: `17.0.12-graal`, `25.0.4.1-graal`. +Oracle's distribution of GraalVM, built on Oracle JDK. Releases carry GraalVM's numbers: `17.0.12-graal`, `25.0.4.1.1-graal`, `25.4.4.1.1-graal`. ### GraalVM Community (`graalce`) @@ -166,9 +166,12 @@ GraalVM CE 8 to 19, Gluon GraalVM before 23, and Mandrel and Liberica NIK before ### Behavior of Oracle GraalVM and GraalVM Community -- Since 25.1 (June 2026) GraalVM puts out a feature release every month next to its 25.0 line: `25.1.3`, `25.2.4`, `25.3.4.1`, `25.4.4.1`. Each is replaced within about a month. Foojay marks them LTS because the JDK they build on is LTS, and `vfox search` leaves the LTS note off them. -- `java@25-graal` installs the newest build of the 25.0 line (`25.0.4.1-graal`), the line Oracle's own `25/latest` download follows. A feature release needs its full version: `java@25.4.4.1-graal`. -- GraalVM Community's 25.0 line ended with `25.0.2`, and the feature releases are all it still ships. `java@25-graalce` installs the newest of them, `25.4.4.1-graalce` on 2026-10-18. +- Since 25.1 (June 2026) GraalVM puts out a feature release every month next to its 25.0 line: `25.1.3`, `25.2.4`, `25.3.4.1`, `25.4.4.1.1`. Each is replaced within about a month. Foojay marks Oracle GraalVM's STS, but GraalVM Community's LTS where it lists them the old way round (see below), as the JDK they build on is LTS. `vfox search` leaves the LTS note off them all. +- Releases are named after GraalVM's own version, which Foojay gives as `distribution_version`. Foojay's `java_version` is the JDK's version, which a feature release shares with the 25.0 line: `25.4.4.1.1-graal`, `25.3.4.1-graal`, `25.0.4.1.1-graal` and `25.0.4.1-graal` all have the `java_version` 25.0.4.1. +- Foojay lists GraalVM Community's feature releases under GraalVM's version too, as it did before October 2026, with downloads from GitHub. So `java@25.4.4.1-graalce`, the name release 0.6.2 gives the 25.4.4.1.1 build, still installs it from GitHub, while `java@25.4.4.1.1-graalce` gets the same file from Oracle's GraalVM Download Service. +- Unreleased: release 0.6.2 names releases after `java_version`, so `java@25.0.4.1-graal` installs the feature release 25.4.4.1.1, `java@25-graal` installs a feature release whenever one is out on the JDK of the newest 25.0 build, and `vfox search java graal` lists no feature release. +- `java@25-graal` installs the newest build of the 25.0 line (`25.0.5-graal` on 2026-10-20), the line Oracle's own `25/latest` download follows. A feature release needs its full version: `java@25.4.4.1.1-graal`. +- GraalVM Community's 25.0 line ended with `25.0.2`, and the feature releases are all it still ships. `java@25-graalce` installs the newest of them, `25.4.4.1.1-graalce` on 2026-10-20. - The plugin sets `JAVA_HOME` and `PATH`, and no `GRAALVM_HOME`. A `GRAALVM_HOME` that points at a GraalVM of your own stays as it is, and the native-image build plugins fall back to `JAVA_HOME` when it is unset. - There is no Windows ARM64 and no Alpine build, and Intel Macs get nothing after 25.0.1. See [platforms.md](platforms.md#graalvm). diff --git a/docs/e2e.md b/docs/e2e.md index 504c6ce..8e113f0 100644 --- a/docs/e2e.md +++ b/docs/e2e.md @@ -82,13 +82,21 @@ JDK, so `java` can only come from what vfox installs: build number: Foojay reads an unencoded `+` as a space and drops the build, and the release's newest build would install instead. 3. On the GraalVM Community rows, Oracle GraalVM's bare major `25-graal` - installs the newest build of its 25.0 line (`25.0.4.1-graal`), the line - Oracle's own `25/latest` download follows, not one of the monthly feature - releases Foojay lists before it (`25.4.4.1-graal`). The builds of 25 that - `vfox search java graal` lists are faked as installed first, so + installs the newest build of its 25.0 line (`25.0.5-graal` on + 2026-10-20), the line Oracle's own `25/latest` download follows, not one + of the monthly feature releases listed before it (`25.4.4.1.1-graal`). + `vfox search java graal` must list those under GraalVM's own version, + and mark the other releases of 25 alike, as in item 5. The plugin used to + name them after the JDK version Foojay has given them since October 2026, + which they share with the 25.0 line: the search then listed no feature + release, and `25.0.4.1-graal` had no LTS note and installed 25.4.4.1.1. + The builds of 25 that the search lists are faked as installed first, so `vfox install` names the one `PreInstall` picked and nothing downloads. GA 25 is left out, as vfox would find `25-graal` itself installed without - asking `PreInstall`. + asking `PreInstall`. The search and `PreInstall` ask Foojay the same + question, so they see the same builds + ([foojay.md](foojay.md#the-versions-of-one-distribution)): a build that + only `PreInstall` saw would download and fail the check. 4. On the Temurin rows, which run on every runner, the distributions the suite checks by name only (`adpt` to `semcrt`, `nameOnly` in `tests/e2e/common`) go by their short names where Foojay has packages of @@ -114,8 +122,11 @@ JDK, so `java` can only come from what vfox installs: `21.0.8+9-tem`). `vfox search java ` (the `Available` hook) lists that same name as installed, noting JavaFX on `-fx` builds. For GraalVM it must also list the feature releases GraalVM ships every month - since 25.1 (`25.4.4.1-graalce`), none of them noted LTS, though Foojay - marks them LTS, as the JDK they build on is; each lasts about a month. + since 25.1 (`25.4.4.1.1-graalce`), none of them noted LTS, though Foojay + marks some of them LTS, as the JDK they build on is; each lasts about a + month. Every other release of a major must carry the same note. The + plugin used to list the feature release 25.4.4.1.1 as `25.0.4.1-graalce`, + without the LTS note `25.0.2-graalce` has. On Windows the list includes JetBrains Runtime 21.0.5, which ships only as tar.gz there. 6. Before any `vfox use`, a `.sdkmanrc` holding the SDKMAN identifier diff --git a/docs/foojay.md b/docs/foojay.md index aead521..1030521 100644 --- a/docs/foojay.md +++ b/docs/foojay.md @@ -24,18 +24,18 @@ https://api.foojay.io/disco/v3.0/packages/jdks | Parameter | Value | | --- | --- | | `distribution` | the long name, see [distributions.md](distributions.md) | -| `version` | empty for `vfox search`, else the version part of what you typed. A `+` is percent-encoded, because Foojay reads a bare one as a space and drops the build. `28.ea.19` becomes `28-ea+19`. Always empty for Mandrel and Liberica NIK | +| `version` | empty for `vfox search`, else the version part of what you typed. A `+` is percent-encoded, because Foojay reads a bare one as a space and drops the build. `28.ea.19` becomes `28-ea+19`. Always empty for Mandrel and Liberica NIK, and for the GA releases of Oracle GraalVM and GraalVM Community | | `distribution_version` | for Mandrel and Liberica NIK only, the version part of what you typed, see [below](#mandrel-and-liberica-nik) | | `architecture` | `aarch64` for arm64, `x64` on Windows x64, `amd64` for x64 elsewhere, and the name vfox reports for other CPUs | | `operating_system` | `linux`, `macos` or `windows` | | `archive_type` | `tar.gz`, and `zip` plus `tar.gz` on Windows. Elsewhere `tar.xz` comes in for Red Hat, and `zip` for OJDKBuild, OpenLogic and Gluon GraalVM | | `lib_c_type` | `glibc` or `musl` on Linux, empty elsewhere | | `directly_downloadable` | always `true` | -| `latest` | `available`, added only when the version is a bare major such as `21`, and never for Mandrel and Liberica NIK | +| `latest` | `available`, added only when the version is a bare major such as `21`, and never for Mandrel, Liberica NIK, Oracle GraalVM and GraalVM Community | Without `latest=available`, `version=21` matches the 21 GA build only (`21+35`). With it Foojay picks the newest 21.x, which is what `java@21-tem` installs. Longer versions match as they are. -The answer is a list of packages. The plugin names a release after `java_version` (Mandrel and Liberica NIK after `distribution_version`), adds `-fx` where `javafx_bundled` is true, marks it LTS where `term_of_support` is `lts`, and prefers a `zip` over a `tar.gz` of the same version on Windows. It leaves out the JREs, installer bundles and RHEL 6 builds that Foojay lists among OJDKBuild's Windows JDKs, and the JDK 11 builds of Gluon GraalVM's 22.x releases, see [distributions.md](distributions.md). For the install it then fetches the package's `links.pkg_info_uri` and downloads its `direct_download_uri`, which is a vendor's own server (GitHub Releases for Temurin). That record also holds a `checksum`. The plugin does not pass it on, so vfox prints `WARNING: Checksum is not provided, skip verify...` and does not verify the download. +The answer is a list of packages. The plugin names a release after `java_version` (Mandrel and Liberica NIK after `distribution_version`, Oracle GraalVM and GraalVM Community after GraalVM's own version), adds `-fx` where `javafx_bundled` is true, marks it LTS where `term_of_support` is `lts`, and prefers a `zip` over a `tar.gz` of the same version on Windows. It leaves out the JREs, installer bundles and RHEL 6 builds that Foojay lists among OJDKBuild's Windows JDKs, and the JDK 11 builds of Gluon GraalVM's 22.x releases, see [distributions.md](distributions.md). For the install it then fetches the package's `links.pkg_info_uri` and downloads its `direct_download_uri`, which is a vendor's own server (GitHub Releases for Temurin). That record also holds a `checksum`. The plugin does not pass it on, so vfox prints `WARNING: Checksum is not provided, skip verify...` and does not verify the download. `vfox search` differs from the raw list in a few ways. It lists a name once where Foojay has two records for it, and it leaves the LTS note off GraalVM's feature releases (see [distributions.md](distributions.md#behavior-of-oracle-graalvm-and-graalvm-community)). It only lists GA releases. @@ -122,7 +122,25 @@ curl -s 'https://api.foojay.io/disco/v3.0/packages/jdks?distribution=temurin&ver {"java_version":"21.0.8+9","distribution_version":"21.0.8","term_of_support":"lts","javafx_bundled":false,"archive_type":"tar.gz","filename":"OpenJDK21U-jdk_x64_linux_hotspot_21.0.8_9.tar.gz"} ``` -`java_version` is Java's version and the one the plugin uses. `distribution_version` is the vendor's own (Zulu's `27.28.101` for Java `27+35`), which the plugin uses for Mandrel and Liberica NIK only, see [above](#mandrel-and-liberica-nik). +`java_version` is Java's version and the one the plugin uses. `distribution_version` is the vendor's own (Zulu's `27.28.101` for Java `27+35`), which the plugin uses for Mandrel and Liberica NIK, see [above](#mandrel-and-liberica-nik), and for Oracle GraalVM and GraalVM Community. + +For Oracle GraalVM and GraalVM Community, since October 2026, `distribution_version` is GraalVM's own version and `java_version` the JDK's, which a monthly feature release shares with a build of the 25.0 line: + +```bash +curl -s 'https://api.foojay.io/disco/v3.0/packages/jdks?distribution=graalvm&version=25.0.4.1&architecture=amd64&operating_system=linux&lib_c_type=glibc&archive_type=tar.gz&directly_downloadable=true' \ + | jq -r '.result[] | [.java_version, .distribution_version, .filename] | join(" ")' +``` + +``` +25.0.4.1 25.4.4.1.1 graalvm-jdk-25i4-25.0.4.1.1_linux-x64_bin.tar.gz +25.0.4.1 25.3.4.1 graalvm-jdk-25i3-25.0.4.1_linux-x64_bin.tar.gz +25.0.4.1 25.0.4.1.1 graalvm-jdk-25.0.4.1.1_linux-x64_bin.tar.gz +25.0.4.1 25.0.4.1 graalvm-jdk-25.0.4.1_linux-x64_bin.tar.gz +``` + +`version` cannot tell those apart, and `distribution_version` matches whole leading components (`25.0.4.1` also lists `25.0.4.1.1`). So the plugin asks for every package, as `vfox search` does, picks the release itself, and names it after GraalVM's version: the newer of a package's `java_version` and `distribution_version`, as GraalVM Community's feature releases are also listed the old way round, with GraalVM's version as `java_version`. `vfox search` lists them newest first by that version, and a file that Foojay lists under two versions once. + +Installing asks what searching asks so that the two see the same builds. Foojay's answers are cached per question, and two questions can get answers of different ages: half an hour after GraalVM 25.0.5 reached Foojay on 2026-10-20, GitHub's runners got it from a lookup with `distribution_version=25` but not from a search. ## Releases and support terms diff --git a/docs/versions.md b/docs/versions.md index c1d74e8..3e0ebb6 100644 --- a/docs/versions.md +++ b/docs/versions.md @@ -27,7 +27,7 @@ The distribution is a short name (`tem`) or a long name (`temurin`). Both lists - A longer version must match a release the distribution lists. `21.0.8-tem` installs `21.0.8+9-tem`, the newest build of 21.0.8. A release nobody published fails with `No JDK found for 21.0.99-tem on linux/amd64`. - A build number selects that build: `21.0.5+63130-jb`. - A release padded with zeros, as SDKMAN writes it, is accepted: `27.0.0-librca` installs `27+36-librca`. -- Oracle GraalVM's bare major installs the newest build of its 25.0 line, not one of the monthly feature releases, which have to be named in full (`25.4.4.1-graal`). See [GraalVM](distributions.md#graalvm-family). +- Oracle GraalVM's bare major installs the newest build of its 25.0 line, not one of the monthly feature releases, which have to be named in full (`25.4.4.1.1-graal`). Unreleased: release 0.6.2 can install a feature release for the bare major. See [GraalVM](distributions.md#graalvm-family). - With `-fx` only JavaFX builds are considered, without it only builds without JavaFX. The two are never mixed. - Mandrel and Liberica NIK go by the GraalVM version they carry, for a bare major too: `java@23-mandrel` installs `23.1.12.1-mandrel`, which runs Java 21. See [Mandrel and Liberica NIK](distributions.md#behavior-of-mandrel-and-liberica-nik). - On Windows a release shipped as zip and as tar.gz installs from the zip. @@ -37,7 +37,7 @@ An install that finds the name already present prints `java@21.0.12.1+1-tem is a ## Installed names -The plugin names an install after the `java_version` that Foojay reports, or the `distribution_version` for Mandrel and Liberica NIK. `vfox search` prints the same names: +The plugin names an install after the `java_version` that Foojay reports, or the `distribution_version` for Mandrel and Liberica NIK, and GraalVM's own version for Oracle GraalVM and GraalVM Community. `vfox search` prints the same names: ``` [-][-fx] @@ -49,6 +49,7 @@ The plugin names an install after the `java_version` that Foojay reports, or the | `25.0.4+9-librca-fx` | Liberica with JavaFX | | `25.0.2+10` | OpenJDK, no suffix | | `17.0.12-graal` | GraalVM, which has no build number | +| `25.4.4.1.1-graal` | GraalVM's version, not `java_version`, for Oracle GraalVM and GraalVM Community, here a monthly feature release, see [GraalVM](distributions.md#behavior-of-oracle-graalvm-and-graalvm-community) | | `8.0.482+8-zulu` | Java 8 is `8.0.+` | | `23.1.12.1-mandrel` | Mandrel, numbered after GraalVM: it runs Java 21 | | `28-ea+19` | early-access build, see below | @@ -115,4 +116,4 @@ The list holds what exists for the machine you run it on, see [platforms.md](pla `vfox install java tem` does not select Temurin: arguments separated by spaces are several SDKs. Write `java@21-tem`. -The LTS note is Foojay's, except that GraalVM's monthly feature releases (`25.4.4.1-graal`) carry none, though Foojay marks them LTS. +The LTS note is Foojay's, except that GraalVM's monthly feature releases (`25.4.4.1.1-graal`) carry none, though Foojay marks some of them LTS. diff --git a/docs/zh-CN/changes.md b/docs/zh-CN/changes.md index 75854bc..fcb3d4c 100644 --- a/docs/zh-CN/changes.md +++ b/docs/zh-CN/changes.md @@ -9,6 +9,8 @@ - 新增 13 个发行版,Foojay 列出的发行版从此全都有了:AdoptOpenJDK(`adpt`、`adptj9`)、Eliya(`eliya`)、OJDKBuild(`ojdkbld`)、OpenLogic(`opnlgc`)、Red Hat(`rdht`)、Semeru 认证版(`semcrt`)、Gluon GraalVM(`gln`),以及基于 JDK 8 到 19 的 GraalVM CE(`grl8`、`grl11`、`grl16`、`grl17`、`grl19`)。Red Hat 从 tar.xz 包安装;Linux 上的 OJDKBuild、macOS 上的 OpenLogic 和 22.1 以前的 Gluon GraalVM 从 zip 包安装。`.sdkmanrc` 里写 SDKMAN 以前给它们的标识符也认,例如 `11.0.10.j9-adpt`、`22.3.3.r17-grl`。见 [distributions.md](distributions.md)。 - Mandrel 和 Liberica NIK 重新按它们沿用的 GraalVM 版本号安装:`java@23.1.12-mandrel` 和 `java@23.1.8-nik` 能装了,`vfox search java mandrel` 列出全部版本,`java@23-mandrel` 装最新的 23.x,即运行 Java 21 的 `23.1.12.1-mandrel`。2026 年 10 月,Foojay 把这个版本号从 `java_version` 挪到了 `distribution_version`,此后 0.6.2 装这样的版本都报 `No JDK found`,每个 Java 大版本只列一个 Mandrel,`java@21-mandrel` 装的是 Mandrel 23.1.8。这段时间里它装上的版本按 Java 版本号命名(`21-mandrel`、`21.0.12.1+2-nik`),vfox 仍会按这些名字找到它们;想要这个写法现在所指的版本,先把旧的卸载。见 [distributions.md](distributions.md#mandrel-与-liberica-nik-的行为)([#106](https://github.com/version-fox/vfox-java/issues/106))。 - `vfox add java`、`vfox update java` 和 `vfox info java` 打印的 Notes 改成了一个 README 链接,README 里列了全部简称,也写了怎样搜索某个发行版。0.6.2 的 Notes 只给了五个简称,没说怎样列出它们的版本,而 `vfox search java` 只列 OpenJDK([vfox#390](https://github.com/version-fox/vfox/issues/390))。 +- Oracle GraalVM 和 GraalVM 社区版改按 GraalVM 自己的版本号命名和查找。从 2026 年 10 月起,Foojay 把这个版本号放在 `distribution_version` 里。0.6.2 仍然读 `java_version`,而它现在是 JDK 的版本号,所以 `java@25.0.4.1-graal` 装上的是特性版本 25.4.4.1.1;只要 25.0 系列最新构建所用的 JDK 上已经出了特性版本,`java@25-graal` 也会装上特性版本;`vfox search java graal` 里也一个特性版本都没有。现在 `java@25-graal` 装的是 25.0 系列的最新构建(2026-10-20 是 `25.0.5-graal`),`java@25.0.4.1-graal` 装的就是 GraalVM 25.0.4.1,那个特性版本要写成 `java@25.4.4.1.1-graal`。`java@25-graalce` 装的是 `25.4.4.1.1-graalce`,从 Oracle 的 GraalVM Download Service 下载;0.6.2 从 GitHub 下载同一个文件,起名 `25.4.4.1-graalce`([#105](https://github.com/version-fox/vfox-java/issues/105))。 +- 在 Foojay 改格式之后用 0.6.2 装的 25.0 版本名,里面可能是特性版本:`25.0.4.1-graal` 是 GraalVM 25.4.4.1.1,`25.0.4-graal` 是 25.2.4,`25.0.3-graal` 是 25.1.3,这几个名字换成 `-graalce` 也一样。名字已经装过的,vfox 不会重装,所以如果 `java -version` 打印的 GraalVM 版本和名字对不上,先 `vfox uninstall` 再重新安装。`25.0.4.1-graalce`、`25.0.4-graalce` 和 `25.0.3-graalce` 现在根本装不上了,因为 GraalVM 社区版的 25.0 系列到 `25.0.2` 为止。 ## 0.6.2(2026-10-18) diff --git a/docs/zh-CN/development.md b/docs/zh-CN/development.md index 9487cb8..84ed3df 100644 --- a/docs/zh-CN/development.md +++ b/docs/zh-CN/development.md @@ -44,7 +44,7 @@ vfox 加载 `metadata.lua`,命令需要时调用相应的钩子,并向钩子 ## 命名 -安装后的版本名是 `[-<简称>][-fx]`,OpenJDK 没有简称,Mandrel 和 Liberica NIK 用 Foojay 的 `distribution_version` 代替 `java_version`([versions.md](versions.md#安装后的版本名))。`lib/distribution_version.lua` 把一个写法解析成版本、发行版和 JavaFX 标志,两种顺序都接受(`21-tem`、`tem-21`)。`lib/installed_version.lua` 把用户输入的版本变成已安装的版本名:先看这个名字本身。只写大版本号时,取这个大版本里已安装的最新版本;其他写法则取这个版本本身,或它的最新构建。`PreUse` 到此为止。`ParseLegacyFile` 还会再退一步,取同一大版本里已安装的最新版本。 +安装后的版本名是 `[-<简称>][-fx]`,OpenJDK 没有简称,Mandrel 和 Liberica NIK 用 Foojay 的 `distribution_version` 代替 `java_version`,`graal` 和 `graalce` 用 GraalVM 自己的版本号([versions.md](versions.md#安装后的版本名))。`lib/distribution_version.lua` 把一个写法解析成版本、发行版和 JavaFX 标志,两种顺序都接受(`21-tem`、`tem-21`)。`lib/installed_version.lua` 把用户输入的版本变成已安装的版本名:先看这个名字本身。只写大版本号时,取这个大版本里已安装的最新版本;其他写法则取这个版本本身,或它的最新构建。`PreUse` 到此为止。`ParseLegacyFile` 还会再退一步,取同一大版本里已安装的最新版本。 SDKMAN 有些写法不同(`jbr`、`21.0.12.fx-zulu`、`21.0.12-fx+1.1-librca`、`28.0.0+ea.19-open`),`hooks/parse_legacy_file.lua` 里的 `plugin_spelling` 会在解析之前把它们改写成插件自己的写法。对 SDKMAN 已经不再提供的发行版,同一文件里的 `sdkman_flavours` 把 SDKMAN 写在版本号里的虚拟机或 JDK 版本换成简称(`22.3.3.r17-grl` 就是 `22.3.3-grl17`)。SDKMAN 给版本补的零由 `installed_version.unpad` 去掉(`27.0.0+36` 就是 `27+36`)。简称与 SDKMAN 不同的地方见 [distributions.md](distributions.md#简称与-sdkman-的不同)。 @@ -55,9 +55,10 @@ SDKMAN 有些写法不同(`jbr`、`21.0.12.fx-zulu`、`21.0.12-fx+1.1-librca` - Windows 在同一次请求里同时要 `zip` 和 `tar.gz`。JetBrains Runtime 只为较新的构建提供 Windows 的 zip。同一版本两种都有时,`PreInstall` 取 zip。 - 在其他系统上,有几个发行版的部分或全部版本没有 tar.gz,`lib/foojay.lua` 里的 `other_archive_type` 会为它们多要一种格式:Red Hat 要 `tar.xz`,OJDKBuild、OpenLogic 和 Gluon GraalVM 要 `zip`。这两种 vfox 都能解压,这些 zip 包也带着 Unix 权限,所以 `bin/java` 仍然可以执行。 - `lib/foojay.lua` 里的 `own_numbers` 列的是 Mandrel 和 Liberica NIK,它们沿用所基于的 GraalVM 的版本号。Foojay 把这个版本号放在 `distribution_version` 里,把 Java 的版本号放在 `java_version` 里(Mandrel 的 `java_version` 只有大版本号),所以查这两个发行版时用 `distribution_version`,`version` 留空。Foojay 按给出的几段数字匹配(查 `23.1.12` 也会返回 23.1.12.1),所以写得更具体的版本,插件只保留与它完全相同的那个;只写大版本号时由插件自己挑最新的,因为 `latest=available` 对它们会挑出旧版本。结果按版本从新到旧排;同一版本有几个 Java 版本的构建时,Java 最新的在前;为同一个 Java 构建了两次时,BellSoft 后一次的构建在前,两次构建只能从文件名区分。之后 `distribution_version` 写进 `java_version`,钩子就能像对其他发行版一样给它们命名、列出它们。 +- Oracle GraalVM 和 GraalVM 社区版按 GraalVM 自己的版本号命名和查找。从 2026 年 10 月起,Foojay 把它放在 `distribution_version` 里,`java_version` 则是 JDK 的版本号。`lib/foojay.lua` 里的 `graalvm_packages` 和 `vfox search` 发同一个请求,查出全部的包,这样安装时看到的构建和搜索列出的一致(见 [foojay.md](foojay.md#某个发行版的所有版本))。它把每个包改用 GraalVM 的版本号命名(`graalvm_version`:取 `java_version` 和 `distribution_version` 中较新的那个,因为 GraalVM 社区版的特性版本还有一份按老办法列的记录),按从新到旧排序;只写大版本号时留下这个大版本的全部包,更具体的版本则只留下它指的那一个,补的零不算。给 `vfox search` 用时,同一个下载只留一条,用两个名字中较新的那个(`25.4.4.1.1`,不是 `25.4.4.1`)。查这两个发行版时不加 `latest=available`,因为它会漏掉版本:2026-10-20 在 Windows 上查 25,结果里有 GraalVM `25.0.4.1`,却没有 `25.0.4.1.1`,两者的 Java 版本都是 25.0.4.1。EA 构建仍按 `version` 查。 - `lib/foojay.lua` 里的 `fixes` 会在钩子拿到结果之前去掉一些包:Foojay 列在 OJDKBuild 的 Windows JDK 里的 JRE、installer bundle 包和 RHEL 6 构建,以及 Gluon GraalVM 22.x 的 JDK 11 构建,它们和 JDK 17 构建共用一个版本号。只写大版本号时,`latest=available` 每种格式只返回一个包,这个包可能正好是要去掉的,所以对这两个发行版,插件会再按那个版本完整地查一次。 - Foojay 里同一个名字有两条记录时,`Available` 只列一次。 -- Foojay 把 GraalVM 每月发布的特性版本(`25.1.3` 起)标成 LTS。`lib/distribution_version.lua` 里的 `is_feature_release` 按次版本号识别它们,`Available` 去掉它们的备注。它只适用于 `graal` 和 `graalce`:Mandrel 和 Liberica NIK 在 24 及以前沿用 GraalVM 的旧版本号,次版本号并不表示特性版本(`mandrel` 的 `23.1.12` 就是 LTS),从 25 起和 JDK 的版本号一致。 +- GraalVM 社区版每月发布的特性版本(`25.1.3` 起),Foojay 按老办法列的那些标成了 LTS,以前 Oracle GraalVM 的也是这样。`lib/distribution_version.lua` 里的 `is_feature_release` 按次版本号识别它们,`Available` 去掉它们的备注。它只适用于 `graal` 和 `graalce`:Mandrel 和 Liberica NIK 在 24 及以前沿用 GraalVM 的旧版本号,次版本号并不表示特性版本(`mandrel` 的 `23.1.12` 就是 LTS),从 25 起和 JDK 的版本号一致。 - 只有 Oracle GraalVM(`graal`)在只写大版本号时,`PreInstall` 才会跳过特性版本,所以装的是 25.0 系列。一个大版本里只有特性版本时,则保留它们。GraalVM 社区版不做这个过滤,因为特性版本就是它现在发布的全部。 - CPU 名字:`arm64` 改成 `aarch64`,Windows 上 `amd64` 改成 `x64`;`darwin` 改成 `macos`。在 Linux 上,由 `ldd --version` 决定是 glibc 还是 musl。 @@ -89,7 +90,7 @@ vfox plugin run . ParseLegacyFile --input '{"filename":".sdkmanrc","filepath":"/ 1. 在 Foojay 里找到它的 id(见 [foojay.md](foojay.md)),并确认它在你关心的平台上有包。 2. 在 `lib/distribution_version.lua` 的 `short_name` 里加一行 `["<简称>"] = ""`。SDKMAN 现在或以前有这个发行版的话,用 SDKMAN 的标识符;没有的话,照 SDKMAN 的习惯起一个:短的厂商名保持原样,长的要缩短,多半是去掉元音(`opnlgc`)。SDKMAN 写法不同的话,在 `hooks/parse_legacy_file.lua` 的 `plugin_spelling` 里做映射,并把这个差别补进 [distributions.md](distributions.md#简称与-sdkman-的不同)。 -3. 如果它的版本号或 LTS 标记需要特殊处理,像 GraalVM 那样,就在 `hooks/available.lua` 和 `hooks/pre_install.lua` 里处理。如果它在 Windows 以外的系统上没有 tar.gz,就把它的格式加进 `lib/foojay.lua` 的 `other_archive_type`;如果 Foojay 给它列的包有的不能照原样安装,就在同一文件的 `fixes` 里加一条修正。 +3. 如果它的版本号需要特殊处理,像 GraalVM 和 Mandrel 那样,就在 `lib/foojay.lua` 读 Foojay 返回结果的地方处理(`graalvm_packages`、`own_numbers`);LTS 标记或只写大版本号时选哪个,在 `hooks/available.lua` 和 `hooks/pre_install.lua` 里处理。如果它在 Windows 以外的系统上没有 tar.gz,就把它的格式加进 `lib/foojay.lua` 的 `other_archive_type`;如果 Foojay 给它列的包有的不能照原样安装,就在同一文件的 `fixes` 里加一条修正。 4. 在 `tests/pre_install_test.lua` 里加一个用例。 5. 更新 `README.md`、[distributions.md](distributions.md) 和 [platforms.md](platforms.md) 里的表,以及对应的中文版(`README_CN.md` 和 `docs/zh-CN/` 下的同名页面)。 6. 如果引入了新的代码路径,也要加进 e2e 矩阵([e2e.md](e2e.md))。矩阵之外的发行版仍会运行所有检查,只是跳过 `java.vendor` 断言和 asdf 风格的 `PreUse` 写法。只新增名字、没有新代码路径的发行版,则改加进 `tests/e2e/common/common.go` 的 `nameOnly`,并写上 Foojay 有它的那些 runner 平台。 diff --git a/docs/zh-CN/distributions.md b/docs/zh-CN/distributions.md index 71c50a6..8264f72 100644 --- a/docs/zh-CN/distributions.md +++ b/docs/zh-CN/distributions.md @@ -130,7 +130,7 @@ IBM Semeru Runtime Certified Edition:Semeru 的 OpenJ9 运行时,通过了 J ### Oracle GraalVM(`graal`) -Oracle 发行的 GraalVM,基于 Oracle JDK。版本号沿用 GraalVM 自己的编号:`17.0.12-graal`、`25.0.4.1-graal`。 +Oracle 发行的 GraalVM,基于 Oracle JDK。版本号沿用 GraalVM 自己的编号:`17.0.12-graal`、`25.0.4.1.1-graal`、`25.4.4.1.1-graal`。 ### GraalVM 社区版(`graalce`) @@ -166,9 +166,12 @@ GraalVM CE 8 到 19、23 以前的 Gluon GraalVM,以及 25 以前的 Mandrel ### Oracle GraalVM 与 GraalVM 社区版的行为 -- 从 25.1(2026 年 6 月)起,GraalVM 在 25.0 系列之外每月发布一个特性版本(feature release):`25.1.3`、`25.2.4`、`25.3.4.1`、`25.4.4.1`,每个大约一个月后就被下一个取代。它们基于的 JDK 是 LTS,所以 Foojay 也把它们标成了 LTS,而 `vfox search` 不给它们加 LTS 备注。 -- `java@25-graal` 装的是 25.0 系列的最新构建(`25.0.4.1-graal`),Oracle 官方的 `25/latest` 下载链接跟的也是这个系列。特性版本要写全版本号:`java@25.4.4.1-graal`。 -- GraalVM 社区版的 25.0 系列到 `25.0.2` 为止,现在只发布特性版本。`java@25-graalce` 装其中最新的一个,截至 2026-10-18 是 `25.4.4.1-graalce`。 +- 从 25.1(2026 年 6 月)起,GraalVM 在 25.0 系列之外每月发布一个特性版本(feature release):`25.1.3`、`25.2.4`、`25.3.4.1`、`25.4.4.1.1`,每个大约一个月后就被下一个取代。Foojay 把 Oracle GraalVM 的特性版本标成 STS;GraalVM 社区版按老办法列的那些(见下文),则因为基于的 JDK 是 LTS,也被标成了 LTS。`vfox search` 一律不给它们加 LTS 备注。 +- 版本名用的是 GraalVM 自己的版本号,也就是 Foojay 的 `distribution_version`。Foojay 的 `java_version` 是 JDK 的版本号,特性版本会和 25.0 系列的某个构建共用同一个值:`25.4.4.1.1-graal`、`25.3.4.1-graal`、`25.0.4.1.1-graal` 和 `25.0.4.1-graal` 的 `java_version` 都是 25.0.4.1。 +- GraalVM 社区版的特性版本,Foojay 还像 2026 年 10 月以前那样,按 GraalVM 的版本号另列了一份,下载地址在 GitHub。所以 0.6.2 给 25.4.4.1.1 构建起的名字 `java@25.4.4.1-graalce` 仍然能从 GitHub 装上这个构建,而 `java@25.4.4.1.1-graalce` 是从 Oracle 的 GraalVM Download Service 下载同一个文件。 +- 未发布:0.6.2 按 `java_version` 命名,所以 `java@25.0.4.1-graal` 装上的是特性版本 25.4.4.1.1;只要 25.0 系列最新构建所用的 JDK 上已经出了特性版本,`java@25-graal` 装上的也是特性版本;`vfox search java graal` 里也没有特性版本。 +- `java@25-graal` 装的是 25.0 系列的最新构建(2026-10-20 是 `25.0.5-graal`),Oracle 官方的 `25/latest` 下载链接跟的也是这个系列。特性版本要写全版本号:`java@25.4.4.1.1-graal`。 +- GraalVM 社区版的 25.0 系列到 `25.0.2` 为止,现在只发布特性版本。`java@25-graalce` 装其中最新的一个,截至 2026-10-20 是 `25.4.4.1.1-graalce`。 - 插件只设置 `JAVA_HOME` 和 `PATH`,不设置 `GRAALVM_HOME`。你设置的、指向自己的 GraalVM 的 `GRAALVM_HOME` 保持原样;没有设置时,native-image 的构建插件会回退到 `JAVA_HOME`。 - 没有 Windows ARM64 和 Alpine 的构建,Intel Mac 上 25.0.1 之后的版本也没有。见 [platforms.md](platforms.md#graalvm)。 diff --git a/docs/zh-CN/e2e.md b/docs/zh-CN/e2e.md index a3b94f9..7482cab 100644 --- a/docs/zh-CN/e2e.md +++ b/docs/zh-CN/e2e.md @@ -32,11 +32,11 @@ Windows 容器的覆盖(`tests/e2e/windows`)无法在开发容器里运行 1. 从指定的渠道安装 vfox,要么是最新发布版(`latest`),要么从源码构建(`main`),然后从当前检出的代码注册本地的 `java` 插件。在 Windows 上,runner 会在 `latest` 组合运行期间把 `main` 构建一次,`main` 的容器由 e2e 镜像加上这个 `vfox.exe` 启动。接着,`vfox info java` 打印的 Notes 要链接到 README,README 里列了各个简称,也写了怎样搜索某个发行版。以前的 Notes 只给了五个简称,没说怎样列出它们的版本([vfox#390](https://github.com/version-fox/vfox/issues/390))。 2. 只写发行版不写版本,无论单独写还是跟在空版本后面(`java@tem`、`java@-tem`),`vfox install` 和 `vfox use` 都应失败,并提示补上版本。以前,`java@tem` 会让钩子报 Lua 错误;`java@-tem` 则让 `PreInstall` 把空版本发给 Foojay,Foojay 列出所有版本,结果装上了最新的。没有任何发行版发布过的版本(`<大版本>.0.99`)应该安装失败,并且 `PreInstall` 要指出它搜索的平台(`linux/amd64`、`windows/arm64` 等),以此确认向 Foojay 查询的就是 runner 所在的平台。已固定版本里从未发布过的构建(`<版本>+99`,例如 `21.0.8+99-tem`)也一样,这证明查询保留了构建号:Foojay 会把没编码的 `+` 当成空格并丢掉构建号,于是会装上这个版本的最新构建。 -3. 在 `graalce` 这一行里,Oracle GraalVM 只写大版本号的 `25-graal` 应该装 25.0 系列的最新构建(`25.0.4.1-graal`),也就是 Oracle 官方 `25/latest` 下载跟的那个系列,而不是 Foojay 列在它前面的某个每月特性版本(比如 `25.4.4.1-graal`)。`vfox search java graal` 列出的 25 的各构建会先被模拟成已安装,这样 `vfox install` 报出的就是 `PreInstall` 选中的那个,而且不会下载任何东西。GA 版本 25 不放进去,因为那样 vfox 会发现 `25-graal` 已安装,根本不会去问 `PreInstall`。 +3. 在 `graalce` 这一行里,Oracle GraalVM 只写大版本号的 `25-graal` 应该装 25.0 系列的最新构建(2026-10-20 是 `25.0.5-graal`),也就是 Oracle 官方 `25/latest` 下载跟的那个系列,而不是列在它前面的某个每月特性版本(比如 `25.4.4.1.1-graal`)。`vfox search java graal` 必须按 GraalVM 自己的版本号列出这些特性版本,25 的其他版本备注要一致,同第 5 条。从 2026 年 10 月起,Foojay 给特性版本标的是 JDK 的版本号,和 25.0 系列共用。插件以前按这个版本号命名,结果搜索里一个特性版本都没有,`25.0.4.1-graal` 也没有 LTS 备注,装上的却是 25.4.4.1.1。搜索列出的 25 的各构建会先被模拟成已安装,这样 `vfox install` 报出的就是 `PreInstall` 选中的那个,而且不会下载任何东西。GA 版本 25 不放进去,因为那样 vfox 会发现 `25-graal` 已安装,根本不会去问 `PreInstall`。搜索和 `PreInstall` 向 Foojay 发的是同一个请求,所以两者看到的构建相同(见 [foojay.md](foojay.md#某个发行版的所有版本)):要是某个构建只有 `PreInstall` 看到了,它就会真的被下载,检查也会失败。 4. 在 `tem` 这几行里(这几行在每个 runner 上都会运行),只按名字检查的发行版(`adpt` 到 `semcrt`,即 `tests/e2e/common` 里的 `nameOnly`),只要 Foojay 在这个 runner 上有它的包,都要能用简称找到:`vfox search java <简称>` 能列出它;把它列出的最新版本模拟成已安装后,`vfox install java@<大版本>-<简称>`(`22-grl17`)要报告这个版本已经安装,所以不会下载任何东西。以前没有这些简称时,这两条命令分别报 `Unsupported distribution` 和 `Could not extract a valid distribution`。 同样在这几行里,Mandrel 和 Liberica NIK 要按它们沿用的 GraalVM 版本号来(`tests/e2e/common` 里的 `ownNumbers`;Windows arm64 上两者都没有):`vfox search java mandrel` 要列出 `23.1.12-mandrel`(Liberica NIK 是 `23.1.8-nik`);把它和搜索结果里最新的 23.x 模拟成已安装后,`vfox install java@mandrel-23.1.12` 和 `vfox install java@23-mandrel` 要报告这两个版本已经安装。2026 年 10 月 Foojay 把这个版本号挪到了 `distribution_version`,插件改读它之前,搜索结果每个 Java 大版本只列一个名字,找不到 `23.1.12-mandrel`,`java@23-mandrel` 装上的是为 Java 23 构建的 Mandrel 24.1.2。 -5. 固定的版本按 `PreInstall` 给它起的名字安装:请求的版本加上构建号(`21.0.8-tem` 安装为 `21.0.8+9-tem`)。`vfox search java <发行版>`(`Available` 钩子)把同一个名字列为已安装,带 JavaFX 的构建会标注 JavaFX。对 GraalVM,它还必须列出自 25.1 起每月发布的特性版本(`25.4.4.1-graalce`),这些版本都不标 LTS(Foojay 标了,因为它们基于的 JDK 是 LTS);每个版本大约只存在一个月。在 Windows 上,列表里要包含 JetBrains Runtime 21.0.5,它在 Windows 上只有 tar.gz。 +5. 固定的版本按 `PreInstall` 给它起的名字安装:请求的版本加上构建号(`21.0.8-tem` 安装为 `21.0.8+9-tem`)。`vfox search java <发行版>`(`Available` 钩子)把同一个名字列为已安装,带 JavaFX 的构建会标注 JavaFX。对 GraalVM,它还必须列出自 25.1 起每月发布的特性版本(`25.4.4.1.1-graalce`),这些版本都不标 LTS(Foojay 给其中一些标了,因为它们基于的 JDK 是 LTS);每个版本大约只存在一个月。同一个大版本里的其他版本,备注必须一致。插件以前把特性版本 25.4.4.1.1 列成 `25.0.4.1-graalce`,没有 `25.0.2-graalce` 那样的 LTS 备注。在 Windows 上,列表里要包含 JetBrains Runtime 21.0.5,它在 Windows 上只有 tar.gz。 6. 在执行任何 `vfox use` 之前,`.sdkmanrc` 要能通过 `ParseLegacyFile` 选中 JDK。测试时,`vfox list java` 里排在最前面的是另一个版本的空安装(`21.0.80+1-tem`,它的版本号以固定的版本开头;vfox 按字符串降序给已安装版本排序)。以前 `ParseLegacyFile` 会把它当成第一个以该版本开头的版本,对补零的版本(见下面第二条),则当成该大版本里最新的版本。要覆盖的写法有: - 不含构建号的 SDKMAN 标识符:`java=21.0.8-tem`、`java=21.0.5-jbr`(SDKMAN 对 JetBrains 的叫法)、`java=25.0.4.fx-librca`(它对 JavaFX 构建的写法)。 - 补零的标识符,`sdk list java` 里有很多这样的:`java=21.0.8.0+9-tem`、`java=25.0.4.0-fx+9-librca`(Foojay 以及安装后的版本名里没有这个零)。 diff --git a/docs/zh-CN/foojay.md b/docs/zh-CN/foojay.md index e588f44..7f090be 100644 --- a/docs/zh-CN/foojay.md +++ b/docs/zh-CN/foojay.md @@ -24,18 +24,18 @@ https://api.foojay.io/disco/v3.0/packages/jdks | 参数 | 取值 | | --- | --- | | `distribution` | 发行版的长名,见 [distributions.md](distributions.md) | -| `version` | `vfox search` 时为空,否则是你输入内容里的版本部分。`+` 会做百分号编码,因为 Foojay 会把没编码的 `+` 当成空格,进而丢掉构建号。`28.ea.19` 会变成 `28-ea+19`。查 Mandrel 和 Liberica NIK 时始终为空 | +| `version` | `vfox search` 时为空,否则是你输入内容里的版本部分。`+` 会做百分号编码,因为 Foojay 会把没编码的 `+` 当成空格,进而丢掉构建号。`28.ea.19` 会变成 `28-ea+19`。查 Mandrel、Liberica NIK,以及 Oracle GraalVM 和 GraalVM 社区版的 GA 版本时始终为空 | | `distribution_version` | 只用于 Mandrel 和 Liberica NIK,是你输入内容里的版本部分,见[下文](#mandrel-与-liberica-nik) | | `architecture` | arm64 用 `aarch64`,Windows x64 用 `x64`,其他系统的 x64 用 `amd64`,其他 CPU 用 vfox 报告的名字 | | `operating_system` | `linux`、`macos` 或 `windows` | | `archive_type` | `tar.gz`;Windows 上是 `zip` 加 `tar.gz`。其他系统上,Red Hat 还要 `tar.xz`,OJDKBuild、OpenLogic 和 Gluon GraalVM 还要 `zip` | | `lib_c_type` | Linux 上是 `glibc` 或 `musl`,其他系统为空 | | `directly_downloadable` | 始终是 `true` | -| `latest` | `available`,仅当版本只有大版本号(如 `21`)时才加;查 Mandrel 和 Liberica NIK 时一律不加 | +| `latest` | `available`,仅当版本只有大版本号(如 `21`)时才加;查 Mandrel、Liberica NIK、Oracle GraalVM 和 GraalVM 社区版时一律不加 | 不加 `latest=available` 时,`version=21` 只匹配 21 的 GA 构建(`21+35`);加上之后,Foojay 会挑出最新的 21.x,`java@21-tem` 装的就是它。更具体的版本则按原样匹配。 -返回的是一个包列表。插件用 `java_version` 给版本命名(Mandrel 和 Liberica NIK 用 `distribution_version`);`javafx_bundled` 为真时加上 `-fx`;`term_of_support` 是 `lts` 时标注 LTS;在 Windows 上,同一版本既有 `zip` 又有 `tar.gz` 时优先用 `zip`。Foojay 混在 OJDKBuild 的 Windows JDK 里的 JRE、installer bundle 包和 RHEL 6 构建,以及 Gluon GraalVM 22.x 的 JDK 11 构建,插件都会略过,见 [distributions.md](distributions.md)。安装时,插件再取这个包的 `links.pkg_info_uri`,并从其中的 `direct_download_uri` 下载,那是厂商自己的服务器(Temurin 是 GitHub Releases)。这条记录里还有 `checksum`,但插件没有把它交给 vfox,所以 vfox 会打印 `WARNING: Checksum is not provided, skip verify...`,下载内容不做校验。 +返回的是一个包列表。插件用 `java_version` 给版本命名(Mandrel 和 Liberica NIK 用 `distribution_version`,Oracle GraalVM 和 GraalVM 社区版用 GraalVM 自己的版本号);`javafx_bundled` 为真时加上 `-fx`;`term_of_support` 是 `lts` 时标注 LTS;在 Windows 上,同一版本既有 `zip` 又有 `tar.gz` 时优先用 `zip`。Foojay 混在 OJDKBuild 的 Windows JDK 里的 JRE、installer bundle 包和 RHEL 6 构建,以及 Gluon GraalVM 22.x 的 JDK 11 构建,插件都会略过,见 [distributions.md](distributions.md)。安装时,插件再取这个包的 `links.pkg_info_uri`,并从其中的 `direct_download_uri` 下载,那是厂商自己的服务器(Temurin 是 GitHub Releases)。这条记录里还有 `checksum`,但插件没有把它交给 vfox,所以 vfox 会打印 `WARNING: Checksum is not provided, skip verify...`,下载内容不做校验。 `vfox search` 与原始列表有几处不同:Foojay 里同一个名字有两条记录时,它只列一次;不给 GraalVM 的特性版本加 LTS 备注(见 [distributions.md](distributions.md#oracle-graalvm-与-graalvm-社区版的行为));只列 GA 版本。 @@ -122,7 +122,25 @@ curl -s 'https://api.foojay.io/disco/v3.0/packages/jdks?distribution=temurin&ver {"java_version":"21.0.8+9","distribution_version":"21.0.8","term_of_support":"lts","javafx_bundled":false,"archive_type":"tar.gz","filename":"OpenJDK21U-jdk_x64_linux_hotspot_21.0.8_9.tar.gz"} ``` -`java_version` 是 Java 的版本号,插件用的就是它。`distribution_version` 是厂商自己的版本号(Java `27+35` 对应 Zulu 的 `27.28.101`),插件只对 Mandrel 和 Liberica NIK 用它,见[上文](#mandrel-与-liberica-nik)。 +`java_version` 是 Java 的版本号,插件用的就是它。`distribution_version` 是厂商自己的版本号(Java `27+35` 对应 Zulu 的 `27.28.101`),插件对 Mandrel 和 Liberica NIK 用它(见[上文](#mandrel-与-liberica-nik)),对 Oracle GraalVM 和 GraalVM 社区版也用它。 + +从 2026 年 10 月起,Oracle GraalVM 和 GraalVM 社区版的 `distribution_version` 是 GraalVM 自己的版本号,`java_version` 则是 JDK 的版本号,而每月的特性版本和 25.0 系列的某个构建会共用同一个 JDK 版本号: + +```bash +curl -s 'https://api.foojay.io/disco/v3.0/packages/jdks?distribution=graalvm&version=25.0.4.1&architecture=amd64&operating_system=linux&lib_c_type=glibc&archive_type=tar.gz&directly_downloadable=true' \ + | jq -r '.result[] | [.java_version, .distribution_version, .filename] | join(" ")' +``` + +``` +25.0.4.1 25.4.4.1.1 graalvm-jdk-25i4-25.0.4.1.1_linux-x64_bin.tar.gz +25.0.4.1 25.3.4.1 graalvm-jdk-25i3-25.0.4.1_linux-x64_bin.tar.gz +25.0.4.1 25.0.4.1.1 graalvm-jdk-25.0.4.1.1_linux-x64_bin.tar.gz +25.0.4.1 25.0.4.1 graalvm-jdk-25.0.4.1_linux-x64_bin.tar.gz +``` + +用 `version` 分不开这几个包,而 `distribution_version` 按开头的完整几段来匹配(`25.0.4.1` 也会列出 `25.0.4.1.1`)。所以插件像 `vfox search` 那样查出全部的包,具体是哪个版本由它自己挑,并按 GraalVM 的版本号命名,也就是每个包的 `java_version` 和 `distribution_version` 中较新的那个:GraalVM 社区版的特性版本还有一份按老办法列的记录,GraalVM 的版本号放在 `java_version` 里。`vfox search` 按这个版本号从新到旧列出,Foojay 在两个版本号下各列一次的同一个文件,只列一次。 + +安装和搜索发的是同一个请求,这样两者看到的构建才一致。Foojay 的结果按请求分别缓存,不同的请求拿到的结果可能新旧不一:2026-10-20 GraalVM 25.0.5 上了 Foojay 半小时后,GitHub 的 runner 用 `distribution_version=25` 查已经有它,搜索的结果里却还没有。 ## 版本与支持期限 diff --git a/docs/zh-CN/versions.md b/docs/zh-CN/versions.md index b8b5de6..ec9089f 100644 --- a/docs/zh-CN/versions.md +++ b/docs/zh-CN/versions.md @@ -27,7 +27,7 @@ vfox install java@21 # 不写发行版:jdk.java.net 上的 Open - 写得更具体时,必须是这个发行版列出的版本。`21.0.8-tem` 装的是 21.0.8 的最新构建 `21.0.8+9-tem`。根本不存在的版本会失败,报 `No JDK found for 21.0.99-tem on linux/amd64`。 - 写了构建号就装那个构建:`21.0.5+63130-jb`。 - SDKMAN 写法里补了零的版本也能识别:`27.0.0-librca` 装的是 `27+36-librca`。 -- Oracle GraalVM 只写大版本号时,装 25.0 系列的最新构建,不装每月发布的特性版本(feature release)。特性版本要写全版本号(`25.4.4.1-graal`)。见 [GraalVM](distributions.md#graalvm-系列)。 +- Oracle GraalVM 只写大版本号时,装 25.0 系列的最新构建,不装每月发布的特性版本(feature release)。特性版本要写全版本号(`25.4.4.1.1-graal`)。未发布:0.6.2 只写大版本号时可能装上特性版本。见 [GraalVM](distributions.md#graalvm-系列)。 - 写了 `-fx` 就只考虑带 JavaFX 的构建,没写就只考虑不带的,两者不会混用。 - Mandrel 和 Liberica NIK 按它们沿用的 GraalVM 版本号来,只写大版本号时也一样:`java@23-mandrel` 装的是 `23.1.12.1-mandrel`,运行的是 Java 21。见 [Mandrel 与 Liberica NIK](distributions.md#mandrel-与-liberica-nik-的行为)。 - 在 Windows 上,同一版本既有 zip 又有 tar.gz 时,装 zip。 @@ -37,7 +37,7 @@ vfox install java@21 # 不写发行版:jdk.java.net 上的 Open ## 安装后的版本名 -安装后的版本名取自 Foojay 返回的 `java_version`,Mandrel 和 Liberica NIK 取的是 `distribution_version`。`vfox search` 里显示的也是这些名字: +安装后的版本名取自 Foojay 返回的 `java_version`,Mandrel 和 Liberica NIK 取的是 `distribution_version`,Oracle GraalVM 和 GraalVM 社区版取的是 GraalVM 自己的版本号。`vfox search` 里显示的也是这些名字: ``` [-<简称>][-fx] @@ -49,6 +49,7 @@ vfox install java@21 # 不写发行版:jdk.java.net 上的 Open | `25.0.4+9-librca-fx` | 带 JavaFX 的 Liberica | | `25.0.2+10` | OpenJDK,没有后缀 | | `17.0.12-graal` | GraalVM,没有构建号 | +| `25.4.4.1.1-graal` | Oracle GraalVM 和 GraalVM 社区版用的是 GraalVM 自己的版本号,不是 `java_version`,这里是一个每月发布的特性版本,见 [GraalVM](distributions.md#oracle-graalvm-与-graalvm-社区版的行为) | | `8.0.482+8-zulu` | Java 8 写成 `8.0.<更新号>+<构建号>` | | `23.1.12.1-mandrel` | Mandrel,按 GraalVM 编号,运行的是 Java 21 | | `28-ea+19` | EA 构建,见下文 | @@ -115,4 +116,4 @@ Available versions: `vfox install java tem` 选的不是 Temurin:用空格分隔的参数表示多个 SDK。请写 `java@21-tem`。 -LTS 备注来自 Foojay,唯独 GraalVM 每月发布的特性版本(`25.4.4.1-graal`)不带这个备注,尽管 Foojay 把它们标成了 LTS。 +LTS 备注来自 Foojay,唯独 GraalVM 每月发布的特性版本(`25.4.4.1.1-graal`)不带这个备注,尽管 Foojay 把其中一些标成了 LTS。 diff --git a/hooks/available.lua b/hooks/available.lua index 7186cce..2556668 100644 --- a/hooks/available.lua +++ b/hooks/available.lua @@ -80,7 +80,8 @@ function PLUGIN:Available(ctx) if not seen[v] then seen[v] = true -- check if version exists - -- Foojay marks GraalVM's feature releases LTS, as the JDK they build on is. + -- Foojay marks GraalVM Community's feature releases LTS where it + -- lists them the old way round, as the JDK they build on is. local lts = jdk.term_of_support == "lts" and not (graalvm[short] and distribution_version_parser.is_feature_release(jdk.java_version)) local note = lts and "LTS" or "" diff --git a/hooks/pre_install.lua b/hooks/pre_install.lua index 03ff9a9..b76b661 100644 --- a/hooks/pre_install.lua +++ b/hooks/pre_install.lua @@ -72,9 +72,9 @@ function PLUGIN:PreInstall(ctx) ) end - -- For Oracle GraalVM, latest=available lists the monthly feature releases - -- (25.4.4.1) before the 25.0 line, which Oracle's own 25/latest download - -- tracks; a bare major takes the newest 25.0 build instead. + -- For Oracle GraalVM, the monthly feature releases (25.4.4.1.1) come + -- before the 25.0 line, which Oracle's own 25/latest download tracks; a + -- bare major takes the newest 25.0 build instead. if distribution_version.distribution.name == "graalvm" and distribution_version.version:match("^%d+$") then local releases = {} for _, candidate in ipairs(filtered_jdks) do diff --git a/lib/foojay.lua b/lib/foojay.lua index 881856f..ff7f70a 100644 --- a/lib/foojay.lua +++ b/lib/foojay.lua @@ -130,6 +130,88 @@ local function encode(value) end)) end +-- Oracle GraalVM and GraalVM Community, whose packages are named after +-- GraalVM's own version (see graalvm_version). +local graalvm = { + graalvm = true, + graalvm_community = true, +} + +--- Returns GraalVM's own version of a GA package. Since 2026-10 Foojay gives +--- it as distribution_version and the JDK's as java_version, so the monthly +--- feature releases share Java versions with the 25.0 line: GraalVM +--- 25.4.4.1.1 (25i4), 25.3.4.1 (25i3), 25.0.4.1.1 and 25.0.4.1 all have the +--- java_version 25.0.4.1. GraalVM Community's feature releases are listed +--- the old way round too (java_version 25.4.4.1, distribution_version +--- 25.0.4.1.1). GraalVM numbers a release after the JDK it builds on, with +--- the feature release in the second component, so GraalVM's version is the +--- newer of the two either way. For those old records that is 25.4.4.1, the +--- name release 0.6.2 gave them, as their java_version stops at four +--- components while the build is GraalVM 25.4.4.1.1. +--- @param package table Foojay package +--- @return string +local function graalvm_version(package) + local version = package.distribution_version or package.java_version + return installed_version.newer(version, package.java_version) and version or package.java_version +end + +--- Lists Oracle GraalVM's or GraalVM Community's packages under GraalVM's +--- own version, newest first: those of version, every one of its major for +--- a bare major, or every one for a search (version ""). Foojay's version +--- parameter matches Java versions only, so every package is asked for, the +--- way a search asks, and picked here. Asking the same question also keeps +--- installing in step with searching: Foojay's answers are cached per +--- question, and half an hour after GraalVM 25.0.5 came out, a search still +--- left it out while a lookup by distribution_version had it. +--- @param request function Foojay lookup taking the version and more query +--- @param version string +--- @return table +local function graalvm_packages(request, version) + local packages = request("", "") or {} + local order = {} + for i, package in ipairs(packages) do + package.java_version = graalvm_version(package) + order[package] = i + end + -- table.sort is not stable, and a long list loses Foojay's order among + -- equal versions, so ties go by that order. + table.sort(packages, function(a, b) + if installed_version.newer(a.java_version, b.java_version) then + return true + end + return not installed_version.newer(b.java_version, a.java_version) and order[a] < order[b] + end) + local result = {} + if version == "" then + -- A download Foojay lists both ways round is searched once, under + -- its full version (25.4.4.1.1, not 25.4.4.1). + local seen = {} + for _, package in ipairs(packages) do + local filename = package.filename or "" + if filename == "" or not seen[filename] then + seen[filename] = true + table.insert(result, package) + end + end + elseif version:match("^%d+$") then + for _, package in ipairs(packages) do + if package.java_version:match("^%d+") == version then + table.insert(result, package) + end + end + else + -- That version alone, not one it is a prefix of (25.0.4.1 is not + -- 25.0.4.1.1), less any zeros padding it, which Foojay drops from + -- Java versions too (25.0 is 25). + for _, package in ipairs(packages) do + if installed_version.unpad(package.java_version) == installed_version.unpad(version) then + table.insert(result, package) + end + end + end + return result +end + foojay.fetchtJdkList = function(distribution, version) local os = RUNTIME.osType local arch = RUNTIME.archType @@ -164,8 +246,8 @@ foojay.fetchtJdkList = function(distribution, version) -- latest=available would look at the major alone. local latest = version:match("^%d+$") and "&latest=available" or "" - local function request(query, latest_query) - local reqUrl = URL:format(encode(query), distribution, arch, archive_types, os, lib_c_type) .. latest_query + local function request(query, extra) + local reqUrl = URL:format(encode(query), distribution, arch, archive_types, os, lib_c_type) .. extra local resp, err = http.get({ url = reqUrl, @@ -181,6 +263,12 @@ foojay.fetchtJdkList = function(distribution, version) return respBody.result end + -- GraalVM goes by its own version, except for early-access builds, whose + -- Java version Foojay matches. + if graalvm[distribution] and not version:find("-ea", 1, true) then + return graalvm_packages(request, version) + end + if own_numbers[distribution] then -- Foojay matches distribution_version by the numbers given, so 23.1.12 -- lists 23.1.12.1 as well, and its latest=available takes an older diff --git a/tests/e2e/common/common.go b/tests/e2e/common/common.go index 2cae103..942158a 100644 --- a/tests/e2e/common/common.go +++ b/tests/e2e/common/common.go @@ -57,7 +57,8 @@ var DefaultVersions = map[string]string{ "jb": "21.0.5", // GraalVM Community: vfox must leave GRAALVM_HOME alone even for // GraalVM (vfox-java#27), and searching must not mark its feature - // releases LTS (vfox-java#95). + // releases LTS (vfox-java#95) nor list them under the JDK's version + // (vfox-java#105). "graalce": "25.0.2", } @@ -81,9 +82,11 @@ var distributions = map[string]distribution{ // OracleGraalVMSpec is Oracle GraalVM's bare major 25. Since 25.1, Oracle // GraalVM puts out a feature release every month on top of its 25.0 line -// (25.1.3 ... 25.4.4.1) and Foojay lists them first, but the bare major must -// install the newest build of the 25.0 line, the one Oracle's own 25/latest -// download follows (vfox-java#97). +// (25.1.3 ... 25.4.4.1.1) and searching lists them first, but the bare major +// must install the newest build of the 25.0 line, the line Oracle's own +// 25/latest download follows (vfox-java#97). Since 2026-10 Foojay lists the +// feature releases under the JDK's version, which the 25.0 line has too, so +// that name took a feature release (vfox-java#105). const OracleGraalVMSpec = "25-graal" // ChecksOracleGraalVM reports whether a combo also checks Oracle GraalVM's @@ -550,13 +553,14 @@ func CheckInstalled(t *testing.T, output, spec string) string { // featureReleaseEntry matches the `vfox search` entry of a GraalVM feature // release. GraalVM ships one every month since 25.1, numbered with the -// feature release train as the minor version (25.4.4.1-graalce), which its +// feature release train as the minor version (25.4.4.1.1-graalce), which its // other releases keep at zero (25.0.2-graalce). var featureReleaseEntry = regexp.MustCompile(`^\d+\.[1-9]`) // FeatureReleaseEntries returns the entries of GraalVM feature releases in -// a GraalVM `vfox search`, without the leading " - ". Foojay marks them LTS, -// as the JDK they build on is, and the Available hook passed that on, though +// a GraalVM `vfox search`, without the leading " - ". Foojay marked them LTS, +// as the JDK they build on is, and still does where it lists GraalVM +// Community's the old way round; the Available hook passed that on, though // each is superseded within a month (vfox-java#95). func FeatureReleaseEntries(output string) []string { var entries []string @@ -569,6 +573,52 @@ func FeatureReleaseEntries(output string) []string { return entries } +// MixedSupportEntries returns the entries of a GraalVM `vfox search`, +// without the leading " - ", that are no feature release but lack the LTS +// note another release of their major carries (25.0.4.1-graal next to +// 25.0.2-graal (LTS)). Foojay marks the 25.0 line LTS and, since 2026-10, +// lists the feature releases under the JDK's version as STS, so a 25.0.x +// entry without the note next to one with it stands for a feature release: +// the plugin named them after that version (vfox-java#105). +func MixedSupportEntries(output string) []string { + var entries []string + lts := map[string]bool{} + for _, line := range strings.Split(StripANSI(output), "\n") { + entry, ok := strings.CutPrefix(strings.TrimRight(line, "\r"), " - ") + if ok && !featureReleaseEntry.MatchString(entry) { + entries = append(entries, entry) + if strings.Contains(entry, "LTS") { + lts[leadingNumber.FindString(entry)] = true + } + } + } + var mixed []string + for _, entry := range entries { + if lts[leadingNumber.FindString(entry)] && !strings.Contains(entry, "LTS") { + mixed = append(mixed, entry) + } + } + return mixed +} + +// CheckGraalVMSearch asserts that a GraalVM `vfox search` lists feature +// releases, none of them noted LTS (see FeatureReleaseEntries), and notes the +// other releases of each major alike (see MixedSupportEntries). Foojay lists +// every feature release since 25.1.3 on each platform the suite runs GraalVM +// on, so the checks cannot pass vacuously. +func CheckGraalVMSearch(t *testing.T, cmd, output string) { + t.Helper() + features := FeatureReleaseEntries(output) + require.NotEmptyf(t, features, "%s lists no feature release\n--- output ---\n%s", cmd, output) + for _, feature := range features { + require.NotContainsf(t, feature, "LTS", "%s must not mark the feature release %s LTS", cmd, feature) + } + require.Emptyf(t, MixedSupportEntries(output), + "%s must note the releases of a major alike, feature releases aside\n--- output ---\n%s", cmd, output) + t.Logf("PASS %s marks no feature release LTS and the other releases of each major alike: %s", + cmd, strings.Join(features, ", ")) +} + // SearchEntry finds the `vfox search` entry listing exactly version // (" - [ ()][ (installed)]") and returns it without the // leading " - ". @@ -601,8 +651,8 @@ var featureRelease = regexp.MustCompile(`^\d+\.[1-9]`) // BareMajorBuilds picks out of the versions `vfox search` lists, newest // first, the builds of a bare major spec's major and distribution -// (25.4.4.1-graal ... 25.0.1-graal for 25-graal), and the one the spec must -// install: the newest that is no feature release (25.0.4.1-graal). The +// (25.4.4.1.1-graal ... 25.0.1-graal for 25-graal), and the one the spec must +// install: the newest that is no feature release (25.0.4.1.1-graal). The // build named like the spec (25-graal, GA 25) is left out: vfox reports a // spec naming an installed version as installed without calling PreInstall. func BareMajorBuilds(searched []string, spec string) (builds []string, want string) { diff --git a/tests/e2e/common/common_test.go b/tests/e2e/common/common_test.go index 08fc014..872e31f 100644 --- a/tests/e2e/common/common_test.go +++ b/tests/e2e/common/common_test.go @@ -16,6 +16,7 @@ package common import ( "slices" + "strings" "testing" ) @@ -319,34 +320,51 @@ func TestDistributionFacts(t *testing.T) { } } +// lineEnding is how output ends its lines: LF, or CRLF as vfox and java write +// them in the Windows containers. A raw string cannot hold a carriage return, +// so the tests write their output with LF and check it with each lineEnding. +type lineEnding struct{ name, newline string } + +var lineEndings = []lineEnding{{"LF", "\n"}, {"CRLF", "\r\n"}} + +// apply returns output, written with LF, with this line ending. +func (e lineEnding) apply(output string) string { + return strings.ReplaceAll(output, "\n", e.newline) +} + func TestJavaProperties(t *testing.T) { // Abridged `java -XshowSettings:properties -version` output, with a - // multi-line value and Windows line endings. - out := "Property settings:\r\n" + - " java.class.path = \r\n" + - " java.home = /root/.vfox/cache/java/v-21.0.8+9-tem/java-21.0.8+9-tem\r\n" + - " java.library.path = /usr/java/packages/lib\r\n" + - " /usr/lib64\r\n" + - " java.vendor = Eclipse Adoptium\r\n" + - " java.version = 21.0.8\r\n" + - " os.arch = amd64\r\n" + - "\r\n" + - "openjdk version \"21.0.8\" 2025-07-15 LTS\r\n" - props := JavaProperties(out) - for k, want := range map[string]string{ - "java.class.path": "", - "java.home": "/root/.vfox/cache/java/v-21.0.8+9-tem/java-21.0.8+9-tem", - "java.library.path": "/usr/java/packages/lib", - "java.vendor": "Eclipse Adoptium", - "java.version": "21.0.8", - "os.arch": "amd64", - } { - if got, ok := props[k]; !ok || got != want { - t.Errorf("%s = %q, %v; want %q", k, got, ok, want) - } - } - if len(props) != 6 { - t.Errorf("props = %v, want 6 entries", props) + // multi-line value. java.class.path is empty, so its line ends in a space. + output := `Property settings: + java.class.path = + java.home = /root/.vfox/cache/java/v-21.0.8+9-tem/java-21.0.8+9-tem + java.library.path = /usr/java/packages/lib + /usr/lib64 + java.vendor = Eclipse Adoptium + java.version = 21.0.8 + os.arch = amd64 + +openjdk version "21.0.8" 2025-07-15 LTS +` + for _, ending := range lineEndings { + t.Run(ending.name, func(t *testing.T) { + props := JavaProperties(ending.apply(output)) + for k, want := range map[string]string{ + "java.class.path": "", + "java.home": "/root/.vfox/cache/java/v-21.0.8+9-tem/java-21.0.8+9-tem", + "java.library.path": "/usr/java/packages/lib", + "java.vendor": "Eclipse Adoptium", + "java.version": "21.0.8", + "os.arch": "amd64", + } { + if got, ok := props[k]; !ok || got != want { + t.Errorf("%s = %q, %v; want %q", k, got, ok, want) + } + } + if len(props) != 6 { + t.Errorf("props = %v, want 6 entries", props) + } + }) } } @@ -374,13 +392,20 @@ func TestListedVersions(t *testing.T) { } func TestFromTarball(t *testing.T) { - tarball := "Preinstalling java@21.0.5+79248-jb...\r\n" + - "Unpacking C:\\Users\\John Doe\\.vfox\\cache\\java\\v-21.0.5+79248-jb\\jbrsdk_jcef-21.0.5-windows-x64-b792.48.tar.gz...\r\n" - if !FromTarball(tarball) { - t.Error("FromTarball missed a .tar.gz unpack") - } - if FromTarball("Unpacking C:\\tmp\\OpenJDK21U-jdk_x64_windows_hotspot_21.0.8_9.zip...\n") { - t.Error("FromTarball matched a .zip unpack") + tarball := `Preinstalling java@21.0.5+79248-jb... +Unpacking C:\Users\John Doe\.vfox\cache\java\v-21.0.5+79248-jb\jbrsdk_jcef-21.0.5-windows-x64-b792.48.tar.gz... +` + zipped := `Unpacking C:\tmp\OpenJDK21U-jdk_x64_windows_hotspot_21.0.8_9.zip... +` + for _, ending := range lineEndings { + t.Run(ending.name, func(t *testing.T) { + if !FromTarball(ending.apply(tarball)) { + t.Error("FromTarball missed a .tar.gz unpack") + } + if FromTarball(ending.apply(zipped)) { + t.Error("FromTarball matched a .zip unpack") + } + }) } } @@ -398,59 +423,129 @@ func TestCheckInstalled(t *testing.T) { } func TestFeatureReleaseEntries(t *testing.T) { - out := "Available versions:\r\n" + - " - 25.4.4.1-graalce (LTS)\r\n" + - " - 25.1.3-graalce\r\n" + - " - 25.0.2-graalce (LTS) (installed)\r\n" + - " - 25-graalce (LTS)\r\n" + - " - 24.0.2-graalce\r\n" + output := `Available versions: + - 25.4.4.1-graalce (LTS) + - 25.1.3-graalce + - 25.0.2-graalce (LTS) (installed) + - 25-graalce (LTS) + - 24.0.2-graalce +` want := []string{"25.4.4.1-graalce (LTS)", "25.1.3-graalce"} - if got := FeatureReleaseEntries(out); !slices.Equal(got, want) { - t.Errorf("FeatureReleaseEntries = %q, want %q", got, want) + for _, ending := range lineEndings { + t.Run(ending.name, func(t *testing.T) { + if got := FeatureReleaseEntries(ending.apply(output)); !slices.Equal(got, want) { + t.Errorf("FeatureReleaseEntries = %q, want %q", got, want) + } + }) + } +} + +func TestMixedSupportEntries(t *testing.T) { + // Abridged `vfox search` output, observed on 2026-10-20 with the plugin + // before and after vfox-java#105: Foojay lists the feature releases + // under the JDK's version, and the name of each stood for a feature + // release first. + before := map[string]string{ + "graal": `Available versions: + - 25.0.4.1-graal + - 25.0.4-graal + - 25.0.3-graal + - 25.0.2-graal (LTS) + - 25-graal (LTS) + - 24.0.2-graal + - 24-graal + - 21.0.12-graal (LTS) +`, + "graalce": `Available versions: + - 25.4.4.1-graalce + - 25.1.3-graalce + - 25.0.4.1-graalce + - 25.0.3-graalce + - 25.0.2-graalce (LTS) (installed) + - 25-graalce (LTS) + - 24.0.2-graalce +`, + } + after := `Available versions: + - 25.4.4.1.1-graal + - 25.1.3-graal + - 25.0.4.1.1-graal (LTS) + - 25.0.4.1-graal (LTS) + - 25-graal (LTS) + - 24.0.2-graal + - 24-graal + - 21.0.12-graal (LTS) (installed) +` + for _, ending := range lineEndings { + t.Run(ending.name, func(t *testing.T) { + for distribution, want := range map[string][]string{ + "graal": {"25.0.4.1-graal", "25.0.4-graal", "25.0.3-graal"}, + "graalce": {"25.0.4.1-graalce", "25.0.3-graalce"}, + } { + if got := MixedSupportEntries(ending.apply(before[distribution])); !slices.Equal(got, want) { + t.Errorf("MixedSupportEntries(%s before) = %q, want %q", distribution, got, want) + } + } + if got := MixedSupportEntries(ending.apply(after)); len(got) != 0 { + t.Errorf("MixedSupportEntries(after) = %q, want none", got) + } + }) } } func TestSearchEntry(t *testing.T) { - out := "Available versions:\n" + - " - 25.0.4+9-librca (LTS)\n" + - " - 25.0.4+9-librca-fx (LTS, JavaFX) (installed)\n" + - " - 25.0.2+10\n" - if got, ok := SearchEntry(out, "25.0.4+9-librca-fx"); !ok || got != "25.0.4+9-librca-fx (LTS, JavaFX) (installed)" { - t.Errorf("SearchEntry(fx) = %q, %v", got, ok) - } - // The non-FX build must not match the FX entry, nor vice versa. - if got, ok := SearchEntry(out, "25.0.4+9-librca"); !ok || got != "25.0.4+9-librca (LTS)" { - t.Errorf("SearchEntry(non-fx) = %q, %v", got, ok) - } - if got, ok := SearchEntry(out, "25.0.2+10"); !ok || got != "25.0.2+10" { - t.Errorf("SearchEntry(bare) = %q, %v", got, ok) - } - if _, ok := SearchEntry(out, "25.0.4"); ok { - t.Error("SearchEntry matched a version prefix") + output := `Available versions: + - 25.0.4+9-librca (LTS) + - 25.0.4+9-librca-fx (LTS, JavaFX) (installed) + - 25.0.2+10 +` + for _, ending := range lineEndings { + t.Run(ending.name, func(t *testing.T) { + out := ending.apply(output) + if got, ok := SearchEntry(out, "25.0.4+9-librca-fx"); !ok || got != "25.0.4+9-librca-fx (LTS, JavaFX) (installed)" { + t.Errorf("SearchEntry(fx) = %q, %v", got, ok) + } + // The non-FX build must not match the FX entry, nor vice versa. + if got, ok := SearchEntry(out, "25.0.4+9-librca"); !ok || got != "25.0.4+9-librca (LTS)" { + t.Errorf("SearchEntry(non-fx) = %q, %v", got, ok) + } + if got, ok := SearchEntry(out, "25.0.2+10"); !ok || got != "25.0.2+10" { + t.Errorf("SearchEntry(bare) = %q, %v", got, ok) + } + if _, ok := SearchEntry(out, "25.0.4"); ok { + t.Error("SearchEntry matched a version prefix") + } + }) } } func TestBareMajorBuilds(t *testing.T) { - // Abridged `vfox search java graal` output, observed on 2026-10-18. - searched := SearchedVersions("Available versions:\r\n" + - " - 25.4.4.1-graal (LTS)\r\n" + - " - 25.1.3-graal (LTS)\r\n" + - " - 25.0.4.1-graal (LTS)\r\n" + - " - 25.0.4-graal (LTS) (installed)\r\n" + - " - 25-graal (LTS)\r\n" + - " - 24.0.2-graal\r\n" + - " - 21.0.12-graal (LTS)\r\n") - if want := []string{"25.4.4.1-graal", "25.1.3-graal", "25.0.4.1-graal", "25.0.4-graal", "25-graal", "24.0.2-graal", "21.0.12-graal"}; !slices.Equal(searched, want) { - t.Fatalf("SearchedVersions = %q, want %q", searched, want) - } - builds, want := BareMajorBuilds(searched, OracleGraalVMSpec) - // GA 25 is named like the spec, so vfox would find it installed before - // asking PreInstall. - if expected := []string{"25.4.4.1-graal", "25.1.3-graal", "25.0.4.1-graal", "25.0.4-graal"}; !slices.Equal(builds, expected) { - t.Errorf("builds = %q, want %q", builds, expected) - } - if want != "25.0.4.1-graal" { - t.Errorf("want = %q, want 25.0.4.1-graal", want) + // Abridged `vfox search java graal` output, observed on 2026-10-20. + output := `Available versions: + - 25.4.4.1.1-graal + - 25.1.3-graal + - 25.0.4.1.1-graal (LTS) + - 25.0.4.1-graal (LTS) (installed) + - 25-graal (LTS) + - 24.0.2-graal + - 21.0.12-graal (LTS) +` + for _, ending := range lineEndings { + t.Run(ending.name, func(t *testing.T) { + searched := SearchedVersions(ending.apply(output)) + if want := []string{"25.4.4.1.1-graal", "25.1.3-graal", "25.0.4.1.1-graal", "25.0.4.1-graal", "25-graal", "24.0.2-graal", "21.0.12-graal"}; !slices.Equal(searched, want) { + t.Fatalf("SearchedVersions = %q, want %q", searched, want) + } + builds, want := BareMajorBuilds(searched, OracleGraalVMSpec) + // GA 25 is named like the spec, so vfox would find it installed + // before asking PreInstall. + if expected := []string{"25.4.4.1.1-graal", "25.1.3-graal", "25.0.4.1.1-graal", "25.0.4.1-graal"}; !slices.Equal(builds, expected) { + t.Errorf("builds = %q, want %q", builds, expected) + } + if want != "25.0.4.1.1-graal" { + t.Errorf("want = %q, want 25.0.4.1.1-graal", want) + } + }) } // GraalVM Community's versions are not Oracle GraalVM's. if builds, want := BareMajorBuilds([]string{"25.4.4.1-graalce", "25.0.2-graalce"}, OracleGraalVMSpec); len(builds) != 0 || want != "" { diff --git a/tests/e2e/linux/setup.go b/tests/e2e/linux/setup.go index 46c63d9..40c092d 100644 --- a/tests/e2e/linux/setup.go +++ b/tests/e2e/linux/setup.go @@ -108,14 +108,16 @@ func checkMissingVersionRejected(ctx context.Context, t *testing.T, ctr tc.Conta // checkGraalVMBareMajor proves Oracle GraalVM's bare major installs the // newest build of its 25.0 line, not one of the monthly feature releases -// Foojay lists before it (see common.OracleGraalVMSpec). The builds of the -// major `vfox search java graal` lists are faked as installed first (see -// common.BareMajorBuilds), so `vfox install` names the one PreInstall picked -// and downloads nothing. +// searching lists before it (see common.OracleGraalVMSpec), which `vfox +// search java graal` must list under their own versions (see +// common.CheckGraalVMSearch). The builds of the major it lists are faked as +// installed first (see common.BareMajorBuilds), so `vfox install` names the +// one PreInstall picked and downloads nothing. func checkGraalVMBareMajor(ctx context.Context, t *testing.T, ctr tc.Container) { t.Helper() spec := common.OracleGraalVMSpec out := execRetry(ctx, t, ctr, "vfox search java graal", "vfox search java graal") + common.CheckGraalVMSearch(t, "vfox search java graal", out) builds, want := common.BareMajorBuilds(common.SearchedVersions(out), spec) require.NotEmptyf(t, want, "vfox search java graal lists no %s build but feature releases\n--- output ---\n%s", spec, out) var dirs, javaDirs []string diff --git a/tests/e2e/linux/verify.go b/tests/e2e/linux/verify.go index 53b43ba..a3af0b9 100644 --- a/tests/e2e/linux/verify.go +++ b/tests/e2e/linux/verify.go @@ -41,8 +41,9 @@ const userGraalVMHome = "/opt/graalvm-for-native-image" // verifySearch checks `vfox search java` (the plugin's Available hook) lists // the installed version under the name PreInstall gave it, marked // installed, with the JavaFX note for -fx builds. For GraalVM it must also -// list feature releases (e.g. 25.4.4.1-graalce), none of them marked LTS -// (see common.FeatureReleaseEntries). +// list feature releases (e.g. 25.4.4.1.1-graalce), none of them marked LTS, +// and mark the other releases of each major alike (see +// common.CheckGraalVMSearch). func verifySearch(ctx context.Context, t *testing.T, ctr tc.Container, distribution, installed string) { t.Helper() cmd := "vfox search java" + common.SearchArgs(distribution) @@ -55,14 +56,7 @@ func verifySearch(ctx context.Context, t *testing.T, ctr tc.Container, distribut } t.Logf("PASS %s lists %s", cmd, entry) if common.GraalVM(distribution) { - // Foojay lists every feature release since 25.1.3 on each platform - // the suite runs GraalVM on, so the check cannot pass vacuously. - features := common.FeatureReleaseEntries(out) - require.NotEmptyf(t, features, "%s lists no feature release\n--- output ---\n%s", cmd, out) - for _, feature := range features { - require.NotContainsf(t, feature, "LTS", "%s must not mark the feature release %s LTS", cmd, feature) - } - t.Logf("PASS %s marks no feature release LTS: %s", cmd, strings.Join(features, ", ")) + common.CheckGraalVMSearch(t, cmd, out) } } diff --git a/tests/e2e/windows/setup.go b/tests/e2e/windows/setup.go index caa1a68..517faa3 100644 --- a/tests/e2e/windows/setup.go +++ b/tests/e2e/windows/setup.go @@ -327,14 +327,16 @@ func checkMissingVersionRejected(ctx context.Context, t *testing.T, ctr tc.Conta // checkGraalVMBareMajor proves Oracle GraalVM's bare major installs the // newest build of its 25.0 line, not one of the monthly feature releases -// Foojay lists before it (see common.OracleGraalVMSpec). The builds of the -// major `vfox search java graal` lists are faked as installed first (see -// common.BareMajorBuilds), so `vfox install` names the one PreInstall picked -// and downloads nothing. +// searching lists before it (see common.OracleGraalVMSpec), which `vfox +// search java graal` must list under their own versions (see +// common.CheckGraalVMSearch). The builds of the major it lists are faked as +// installed first (see common.BareMajorBuilds), so `vfox install` names the +// one PreInstall picked and downloads nothing. func checkGraalVMBareMajor(ctx context.Context, t *testing.T, ctr tc.Container, box slotBox) { t.Helper() spec := common.OracleGraalVMSpec out := execRetry(ctx, t, ctr, "vfox search java graal", "vfox search java graal", box.vars...) + common.CheckGraalVMSearch(t, "vfox search java graal", out) builds, want := common.BareMajorBuilds(common.SearchedVersions(out), spec) require.NotEmptyf(t, want, "vfox search java graal lists no %s build but feature releases\n--- output ---\n%s", spec, out) var create, remove []string diff --git a/tests/e2e/windows/verify.go b/tests/e2e/windows/verify.go index d7c83f2..39a057c 100644 --- a/tests/e2e/windows/verify.go +++ b/tests/e2e/windows/verify.go @@ -43,8 +43,9 @@ const userGraalVMHome = `C:\graalvm-for-native-image` // the installed version under the name PreInstall gave it, marked // installed, with the JavaFX note for -fx builds. That includes releases // shipped only as tar.gz, such as JetBrains Runtime 21.0.5 (vfox-java#72). -// For GraalVM it must also list feature releases (e.g. 25.4.4.1-graalce), -// none of them marked LTS (see common.FeatureReleaseEntries). +// For GraalVM it must also list feature releases (e.g. 25.4.4.1.1-graalce), +// none of them marked LTS, and mark the other releases of each major alike +// (see common.CheckGraalVMSearch). func verifySearch(ctx context.Context, t *testing.T, ctr tc.Container, box slotBox, distribution, installed string) { t.Helper() cmd := "vfox search java" + common.SearchArgs(distribution) @@ -57,14 +58,7 @@ func verifySearch(ctx context.Context, t *testing.T, ctr tc.Container, box slotB } t.Logf("PASS %s lists %s", cmd, entry) if common.GraalVM(distribution) { - // Foojay lists every feature release since 25.1.3 on each platform - // the suite runs GraalVM on, so the check cannot pass vacuously. - features := common.FeatureReleaseEntries(out) - require.NotEmptyf(t, features, "%s lists no feature release\n--- output ---\n%s", cmd, out) - for _, feature := range features { - require.NotContainsf(t, feature, "LTS", "%s must not mark the feature release %s LTS", cmd, feature) - } - t.Logf("PASS %s marks no feature release LTS: %s", cmd, strings.Join(features, ", ")) + common.CheckGraalVMSearch(t, cmd, out) } } diff --git a/tests/pre_install_test.lua b/tests/pre_install_test.lua index 2a74206..e1585df 100644 --- a/tests/pre_install_test.lua +++ b/tests/pre_install_test.lua @@ -43,6 +43,76 @@ local function file(jdk, filename, distribution_version) return jdk end +-- An Oracle GraalVM or GraalVM Community package as Foojay listed them on +-- 2026-10-20: the JDK's version, GraalVM's (distribution_version), the term +-- of support and the file, on Linux x64 unless the file says otherwise. +-- Where Foojay lists a file twice, host tells the two downloads apart. +local function graal(java_version, graalvm_version, term, filename, host) + local jdk = file(pkg(java_version, false, filename:match("%.zip$") and "zip" or nil), filename, graalvm_version) + jdk.term_of_support = term + jdk.host = host + if host then + jdk.links.pkg_info_uri = IDS .. host .. "/" .. filename + end + return jdk +end + +-- Oracle GraalVM on Linux x64, in Foojay's order, observed on 2026-10-20 +-- before 25.0.5 came out; abridged before 25. The feature releases share Java +-- versions with the 25.0 line, and come first for each. Most files are listed +-- twice, and GA 25 comes as two files. +local graalvm_linux = { + graal("25.0.4.1", "25.4.4.1.1", "sts", "graalvm-jdk-25i4-25.0.4.1.1_linux-x64_bin.tar.gz"), + graal("25.0.4.1", "25.3.4.1", "sts", "graalvm-jdk-25i3-25.0.4.1_linux-x64_bin.tar.gz"), + graal("25.0.4.1", "25.0.4.1.1", "lts", "graalvm-jdk-25.0.4.1.1_linux-x64_bin.tar.gz"), + graal("25.0.4.1", "25.0.4.1", "lts", "graalvm-jdk-25.0.4.1_linux-x64_bin.tar.gz"), + graal("25.0.4", "25.2.4", "sts", "graalvm-jdk-25i2-25.0.4_linux-x64_bin.tar.gz"), + graal("25.0.4", "25.0.4", "lts", "graalvm-jdk-25.0.4_linux-x64_bin.tar.gz"), + graal("25.0.4", "25.0.4", "lts", "graalvm-jdk-25.0.4_linux-x64_bin.tar.gz"), + graal("25.0.3", "25.1.3", "sts", "graalvm-jdk-25i1-25.0.3_linux-x64_bin.tar.gz"), + graal("25.0.3", "25.0.3", "lts", "graalvm-jdk-25.0.3_linux-x64_bin.tar.gz"), + graal("25.0.3", "25.0.3", "lts", "graalvm-jdk-25.0.3_linux-x64_bin.tar.gz"), + graal("25.0.2", "25.0.2", "lts", "graalvm-jdk-25.0.2_linux-x64_bin.tar.gz"), + graal("25.0.2", "25.0.2", "lts", "graalvm-jdk-25.0.2_linux-x64_bin.tar.gz"), + graal("25.0.1", "25.0.1", "lts", "graalvm-jdk-25.0.1_linux-x64_bin.tar.gz"), + graal("25.0.1", "25.0.1", "lts", "graalvm-jdk-25.0.1_linux-x64_bin.tar.gz"), + graal("25", "25", "lts", "graalvm-jdk-25.0.0_linux-x64_bin.tar.gz"), + graal("25", "25", "lts", "graalvm-jdk-25_linux-x64_bin.tar.gz"), + graal("24.0.2", "24.0.2", "sts", "graalvm-jdk-24.0.2_linux-x64_bin.tar.gz"), + graal("21.0.12", "21.0.12", "lts", "graalvm-jdk-21.0.12_linux-x64_bin.tar.gz"), + graal("17.0.12", "17.0.12", "lts", "graalvm-jdk-17.0.12_linux-x64_bin.tar.gz"), + graal("17.0.11", "17.0.11", "lts", "graalvm-jdk-17.0.11_linux-x64_bin.tar.gz"), +} + +-- GraalVM Community on Linux x64, observed on 2026-10-20; abridged before 25. +-- Its feature releases are listed under GraalVM's version, as they used to +-- be, with downloads from GitHub, and again under the JDK's, as Oracle +-- GraalVM's are, from Oracle's GraalVM Download Service; its 25.0 line ended +-- with 25.0.2. +local graalvm_community_linux = { + graal("25.4.4.1", "25.0.4.1.1", "lts", "graalvm-community-jdk-25i4-25.0.4.1.1_linux-x64_bin.tar.gz", "github"), + graal("25.3.4.1", "25.0.4.1", "lts", "graalvm-community-jdk-25i3-25.0.4.1_linux-x64_bin.tar.gz", "github"), + graal("25.2.4", "25.0.4", "lts", "graalvm-community-jdk-25i2-25.0.4_linux-x64_bin.tar.gz", "github"), + graal("25.1.3", "25.0.3", "lts", "graalvm-community-jdk-25i1-25.0.3_linux-x64_bin.tar.gz", "github"), + graal("25.0.4.1", "25.4.4.1.1", "sts", "graalvm-community-jdk-25i4-25.0.4.1.1_linux-x64_bin.tar.gz", "gds"), + graal("25.0.4.1", "25.3.4.1", "sts", "graalvm-community-jdk-25i3-25.0.4.1_linux-x64_bin.tar.gz", "gds"), + graal("25.0.4", "25.2.4", "sts", "graalvm-community-jdk-25i2-25.0.4_linux-x64_bin.tar.gz", "gds"), + graal("25.0.3", "25.1.3", "sts", "graalvm-ce-jdk-25i1-25.0.3_linux-x64_bin.tar.gz", "gds"), + graal("25.0.2", "25.0.2", "lts", "graalvm-community-jdk-25.0.2_linux-x64_bin.tar.gz"), + graal("25", "25", "lts", "graalvm-community-jdk-25.0.0_linux-x64_bin.tar.gz"), + graal("24.0.2", "24.0.2", "sts", "graalvm-community-jdk-24.0.2_linux-x64_bin.tar.gz"), +} + +-- The same packages for windows/amd64, where Foojay lists them as zips. +local function zips(list) + local result = {} + for _, jdk in ipairs(list) do + local filename = jdk.filename:gsub("_linux%-x64_bin%.tar%.gz$", "_windows-x64_bin.zip") + table.insert(result, graal(jdk.java_version, jdk.distribution_version, jdk.term_of_support, filename, jdk.host)) + end + return result +end + -- Foojay answers observed on 2026-10-17 for linux/amd64 tar.gz packages, keyed -- by "[ ][ ]", and for windows/amd64 zip and -- tar.gz packages, keyed by "windows [ ][ ]" @@ -53,27 +123,8 @@ local foojay = { ["temurin 21.0.8"] = { pkg("21.0.8+9") }, -- latest=available looks at the major alone. ["temurin 21.0.8 available"] = { pkg("21.0.12.1+1") }, - ["graalvm 17"] = {}, - ["graalvm 17 available"] = { pkg("17.0.12") }, - -- Observed on 2026-10-18: Oracle GraalVM's monthly feature releases come - -- before its 25.0 line, and so do GraalVM Community's, whose 25.0 line - -- ended with 25.0.2. - ["graalvm 25 available"] = { - pkg("25.4.4.1"), - pkg("25.3.4.1"), - pkg("25.2.4"), - pkg("25.1.3"), - pkg("25.0.4.1"), - pkg("25.0.4"), - }, - ["graalvm 25.4.4.1"] = { pkg("25.4.4.1") }, - ["graalvm_community 25 available"] = { - pkg("25.4.4.1"), - pkg("25.3.4.1"), - pkg("25.2.4"), - pkg("25.1.3"), - pkg("25.0.2"), - }, + -- Oracle GraalVM's early-access builds are asked for by version. + ["graalvm 26-ea+13"] = { graal("26-ea+13", "26", "sts", "graalvm-jdk-26.0.0-ea.13_linux-x64_bin.tar.gz") }, ["zulu 21"] = { pkg("21+35"), pkg("21+35", true) }, ["zulu 21 available"] = { pkg("21.0.12.1+1"), pkg("21.0.12.1+1", true) }, ["openjdk 21"] = { pkg("21+35") }, @@ -87,23 +138,18 @@ local foojay = { -- Searching asks for no version; abridged. ["openjdk"] = { pkg("25.0.2+10"), pkg("21.0.2+13") }, ["temurin"] = { pkg("25.0.2+10"), pkg("21.0.8+9") }, - -- Observed on 2026-10-18: Foojay marks GraalVM's feature releases LTS, as - -- 25.0 is; abridged. - ["graalvm"] = { lts(pkg("25.4.4.1")), lts(pkg("25.1.3")), lts(pkg("25.0.4.1")), lts(pkg("25")), pkg("24.0.2") }, - ["graalvm_community"] = { lts(pkg("25.4.4.1")), lts(pkg("25.0.2")) }, + -- Installing Oracle GraalVM or GraalVM Community asks for every package, + -- as searching does. + ["graalvm"] = graalvm_linux, + ["graalvm_community"] = graalvm_community_linux, -- JetBrains Runtime ships Windows zips for its newer builds only, and Foojay -- lists a version's zip and tar.gz in either order; abridged. ["windows jetbrains 21"] = { pkg("21+2121") }, ["windows jetbrains 21 available"] = { pkg("21.0.11+1163116"), pkg("21.0.11+1163116", false, "zip") }, ["windows jetbrains 21.0.5"] = { pkg("21.0.5+79248"), pkg("21.0.5+76240") }, - ["windows graalvm 25 available"] = { - pkg("25.4.4.1", false, "zip"), - pkg("25.3.4.1", false, "zip"), - pkg("25.2.4", false, "zip"), - pkg("25.1.3", false, "zip"), - pkg("25.0.4.1", false, "zip"), - pkg("25.0.4", false, "zip"), - }, + -- Windows lists the same GraalVM packages as zips. + ["windows graalvm"] = zips(graalvm_linux), + ["windows graalvm_community"] = zips(graalvm_community_linux), ["windows jetbrains"] = { pkg("21.0.11+1163116"), pkg("21.0.11+1163116", false, "zip"), @@ -367,6 +413,8 @@ local function check(version, expected, latest, archive_type) if url:sub(1, #JDKS) == JDKS then lookups = lookups + 1 assert((url:find("&latest=available", 1, true) ~= nil) == latest, version .. ": unexpected lookup " .. url) + -- Only Mandrel and Liberica NIK ask by distribution_version. + assert(not url:find("distribution_version=", 1, true), version .. ": unexpected lookup " .. url) end end assert(lookups > 0, version .. ": no Foojay lookup") @@ -398,6 +446,34 @@ local function check_release_lookup(version, expected, release, filename) cases = cases + 1 end +-- Installs version of Oracle GraalVM or GraalVM Community from the named file. +-- The plugin asks Foojay the question searching asks, as Foojay's answers are +-- cached per question and two of them can disagree for a while. +local function check_graalvm(version, expected, filename) + local short = require("distribution_version").parse_version(version).distribution.short_name + requests = {} + PLUGIN:Available({ args = { short } }) + local searched = requests[1] + requests = {} + local result = PLUGIN:PreInstall({ version = version }) + assert(result.version == expected, version .. ": expected " .. expected .. ", got " .. tostring(result.version)) + assert( + result.url:sub(-#filename - 1) == "/" .. filename, + version .. ": expected " .. filename .. ", got " .. result.url + ) + local lookups = {} + for _, url in ipairs(requests) do + if url:sub(1, #JDKS) == JDKS then + table.insert(lookups, url) + end + end + assert( + #lookups == 1 and lookups[1] == searched, + version .. ": asked " .. table.concat(lookups, " ") .. ", searching asks " .. searched + ) + cases = cases + 1 +end + -- A bare major installs its newest release, not the GA build. check("21-tem", "21.0.12.1+1-tem", true) check("temurin-21", "21.0.12.1+1-tem", true) @@ -405,14 +481,46 @@ check("21", "21.0.2+13", true) check("21-zulu", "21.0.12.1+1-zulu", true) check("21-zulu-fx", "21.0.12.1+1-zulu-fx", true) -- Foojay lists no GraalVM 17 GA build, so this found nothing before. -check("17-graal", "17.0.12-graal", true) +check_graalvm("17-graal", "17.0.12-graal", "graalvm-jdk-17.0.12_linux-x64_bin.tar.gz") -- Oracle GraalVM's bare major takes the newest build of its 25.0 line, not a -- monthly feature release, which installs when named in full. -check("25-graal", "25.0.4.1-graal", true) -check("graalvm-25", "25.0.4.1-graal", true) -check("25.4.4.1-graal", "25.4.4.1-graal", false) +check_graalvm("25-graal", "25.0.4.1.1-graal", "graalvm-jdk-25.0.4.1.1_linux-x64_bin.tar.gz") +check_graalvm("graalvm-25", "25.0.4.1.1-graal", "graalvm-jdk-25.0.4.1.1_linux-x64_bin.tar.gz") +check_graalvm("25.4.4.1.1-graal", "25.4.4.1.1-graal", "graalvm-jdk-25i4-25.0.4.1.1_linux-x64_bin.tar.gz") +-- Oracle GraalVM and GraalVM Community go by GraalVM's own version, which +-- Foojay gives as distribution_version since 2026-10-20. A version names one +-- release, though 25.0.4.1 shares its Java version with 25.0.4.1.1 and two +-- feature releases, which came first and installed (vfox-java#105). +check_graalvm("25.0.4.1-graal", "25.0.4.1-graal", "graalvm-jdk-25.0.4.1_linux-x64_bin.tar.gz") +check_graalvm("25.0.4.1.1-graal", "25.0.4.1.1-graal", "graalvm-jdk-25.0.4.1.1_linux-x64_bin.tar.gz") +check_graalvm("25.0.4-graal", "25.0.4-graal", "graalvm-jdk-25.0.4_linux-x64_bin.tar.gz") +check_graalvm("25.0.3-graal", "25.0.3-graal", "graalvm-jdk-25.0.3_linux-x64_bin.tar.gz") +-- Zeros padding the release are left out, as Foojay leaves them out of Java +-- versions. +check_graalvm("25.0.4.1.0-graal", "25.0.4.1-graal", "graalvm-jdk-25.0.4.1_linux-x64_bin.tar.gz") +check_graalvm("25.0-graal", "25-graal", "graalvm-jdk-25.0.0_linux-x64_bin.tar.gz") -- GraalVM Community's feature releases are the only ones it still updates. -check("25-graalce", "25.4.4.1-graalce", true) +-- The newest comes from the GraalVM Download Service, where Foojay links its +-- record under the JDK's version. Foojay lists them under GraalVM's version +-- as well, as it used to, with GitHub downloads, so 25.4.4.1-graalce still +-- installs from there, and a release with a record of each kind installs +-- from the one Foojay lists first. +check_graalvm("25-graalce", "25.4.4.1.1-graalce", "gds/graalvm-community-jdk-25i4-25.0.4.1.1_linux-x64_bin.tar.gz") +check_graalvm( + "25.4.4.1-graalce", + "25.4.4.1-graalce", + "github/graalvm-community-jdk-25i4-25.0.4.1.1_linux-x64_bin.tar.gz" +) +check_graalvm("25.3.4.1-graalce", "25.3.4.1-graalce", "github/graalvm-community-jdk-25i3-25.0.4.1_linux-x64_bin.tar.gz") +check_graalvm("25.0.2-graalce", "25.0.2-graalce", "graalvm-community-jdk-25.0.2_linux-x64_bin.tar.gz") +-- Its 25.0 line ended with 25.0.2, so Foojay's Java version 25.0.4.1 names +-- no release of it, and Oracle GraalVM has no 25.4.4.1. +for distribution, version in pairs({ graalvm_community = "25.0.4.1", graalvm = "25.4.4.1" }) do + local found = require("foojay").fetchtJdkList(distribution, version) + assert(#found == 0, distribution .. " " .. version .. ": found " .. tostring(found[1] and found[1].filename)) +end +-- Early-access builds go by their Java version. +check("26.ea.13-graal", "26-ea+13-graal", false) -- Anything longer than a major still matches exactly. check("21.0.8-tem", "21.0.8+9-tem", false) check("28.ea", "28-ea+19", false) @@ -448,11 +556,12 @@ assert(all["25.0.2+10-tem"] and all["21.0.8+9-tem"], "Available all: Temurin ver -- GraalVM's other releases keep it, and so do Mandrel's, though their GraalVM -- numbers have a non-zero minor up to 24. for version, note in pairs({ - ["25.4.4.1-graal"] = "", + ["25.4.4.1.1-graal"] = "", ["25.1.3-graal"] = "", ["25.0.4.1-graal"] = "LTS", ["25-graal"] = "LTS", - ["25.4.4.1-graalce"] = "", + ["25.4.4.1.1-graalce"] = "", + ["25.3.4.1-graalce"] = "", ["25.0.2-graalce"] = "LTS", ["23.1.12-mandrel"] = "LTS", }) do @@ -468,7 +577,30 @@ end RUNTIME.osType = "windows" assert(check("21-jb", "21.0.11+1163116-jb", true, "zip") == 1, "21-jb: expected one lookup") assert(check("21.0.5-jb", "21.0.5+79248-jb", false, "tar.gz") == 1, "21.0.5-jb: expected one lookup") -check("25-graal", "25.0.4.1-graal", true, "zip") +check_graalvm("25-graal", "25.0.4.1.1-graal", "graalvm-jdk-25.0.4.1.1_windows-x64_bin.zip") +check_graalvm("25-graalce", "25.4.4.1.1-graalce", "gds/graalvm-community-jdk-25i4-25.0.4.1.1_windows-x64_bin.zip") +-- Searching GraalVM lists each release once, under GraalVM's own version and +-- newest first. Of the releases of 25 compared here, the 25.0 line alone has +-- the LTS note. GraalVM Community's 25i4 is listed as 25.4.4.1.1, though +-- Foojay has it as 25.4.4.1 too. +for short, want in pairs({ + graal = "25.4.4.1.1-graal, 25.3.4.1-graal, 25.2.4-graal, 25.1.3-graal, 25.0.4.1.1-graal (LTS), " + .. "25.0.4.1-graal (LTS), 25.0.4-graal (LTS), 25.0.3-graal (LTS), 25.0.2-graal (LTS), " + .. "25.0.1-graal (LTS), 25-graal (LTS)", + graalce = "25.4.4.1.1-graalce, 25.3.4.1-graalce, 25.2.4-graalce, 25.1.3-graalce, 25.0.2-graalce (LTS), " + .. "25-graalce (LTS)", +}) do + for _, query in ipairs({ short, "all" }) do + local entries = {} + for _, item in ipairs(PLUGIN:Available({ args = { query } })) do + if item.version:sub(-#short - 1) == "-" .. short and item.version:match("^25") then + table.insert(entries, item.version .. (item.note == "" and "" or " (" .. item.note .. ")")) + end + end + local got = table.concat(entries, ", ") + assert(got == want, ("Available %s on Windows lists %s, want %s"):format(query, got, want)) + end +end local jb = {} for _, item in ipairs(PLUGIN:Available({ args = { "jb" } })) do table.insert(jb, item.version)