Skip to content

Commit 8061bf2

Browse files
authored
fix: match SDKMAN's zero-padded identifiers in .sdkmanrc to the installed version (#85)
1 parent 00ced99 commit 8061bf2

8 files changed

Lines changed: 126 additions & 32 deletions

File tree

‎docs/e2e.md‎

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,16 @@ JDK, so `java` can only come from what vfox installs:
8080
without the build number selects the JDK through `ParseLegacyFile`:
8181
`java=21.0.8-tem`, `java=21.0.5-jbr` (SDKMAN's name for JetBrains) or
8282
`java=25.0.4.fx-librca` (its spelling of JavaFX builds). So does one
83-
holding the installed version (`java=21.0.8+9-tem`). Both run next to an
84-
empty install of another release whose version starts with the pinned
85-
one (`21.0.80+1-tem`): vfox sorts installed versions as strings,
86-
descending, so `vfox list java` must show it first, and
87-
`ParseLegacyFile` used to take it as the first one starting with the
88-
release. On Windows the file is written with CRLF line endings.
83+
padding the release with a zero, as `sdk list java` shows many:
84+
`java=21.0.8.0+9-tem`, `java=25.0.4.0-fx+9-librca` (Foojay, and so the
85+
installed name, has no such zero). So does one holding the installed
86+
version (`java=21.0.8+9-tem`). All run next to an empty install of
87+
another release whose version starts with the pinned one
88+
(`21.0.80+1-tem`): vfox sorts installed versions as strings, descending,
89+
so `vfox list java` must show it first. `ParseLegacyFile` used to take it
90+
as the first one starting with the release, and, for the padded release,
91+
as the newest release of the major. On Windows the file is written with
92+
CRLF line endings.
8993
5. After `vfox use --global`, the JDK on `PATH` reports the requested
9094
`java.version`, the runner's `os.arch` (`amd64` / `aarch64`, which also
9195
catches an x64 JDK emulated on Windows ARM64) and the distribution's

‎hooks/parse_legacy_file.lua‎

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,12 @@ function PLUGIN:ParseLegacyFile(ctx)
6565
return {}
6666
end
6767

68-
local version = strings.split(fullVersion, "+")[1]
68+
--- SDKMAN pads the release with zeros that Foojay, and so the installed
69+
--- name, leaves out: 27.0.0+36 is 27+36, 25.0.1.0+8 is 25.0.1+8.
70+
local version, build = fullVersion:match("^([^+]*)(.*)$")
71+
while version:find("%.0$") do
72+
version = version:sub(1, -3)
73+
end
6974
local majorVersion = strings.split(version, ".")[1]
7075

7176
--- Installed versions end in -<vendor> (OpenJDK's in nothing), then -fx for
@@ -81,14 +86,16 @@ function PLUGIN:ParseLegacyFile(ctx)
8186
installed[v] = true
8287
end
8388

84-
--- The version itself (21.0.5+63130-jb), else the newest build of the
85-
--- release, as SDKMAN's build numbers count its own rebuilds
89+
--- The version itself (21.0.5+63130-jb), else the release itself or its
90+
--- newest build, as SDKMAN's build numbers count its own rebuilds
8691
--- (21.0.12+1.1-tem), else the newest installed release of the major
87-
--- (21.0.12+8-tem for 21.0.3-tem).
88-
local name = fullVersion .. suffix
92+
--- (21.0.12+8-tem for 21.0.3-tem). A bare major (21-tem) skips the
93+
--- release, taking the newest one as vfox use java@21-tem does, while
94+
--- SDKMAN's 21.0.0 still names the release 21 (21+35-tem).
95+
local name = version .. build .. suffix
8996
return {
9097
version = installed[name] and name
91-
or installed_version.find(installed, version, suffix)
92-
or installed_version.newest(installed, majorVersion .. ".", "^[%d.+]*$", suffix),
98+
or not fullVersion:match("^%d+$") and installed_version.release(installed, version, suffix)
99+
or installed_version.major(installed, majorVersion, suffix),
93100
}
94101
end

‎lib/installed_version.lua‎

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,26 @@ function installed_version.newest(installed, prefix, rest, suffix)
6262
return best
6363
end
6464

65+
--- Returns the installed version of a release given without a build number,
66+
--- or nil: the release itself (17.0.12-graal), else its newest build
67+
--- (21.0.8+9-tem for 21.0.8, 27+36-librca for 27), but never another release
68+
--- (not 21.0.12.1+1 for 21.0.12, nor 21.0.12+8 for 21.0.1).
69+
function installed_version.release(installed, release, suffix)
70+
local name = release .. suffix
71+
if installed[name] then
72+
return name
73+
end
74+
return installed_version.newest(installed, release, "^%+%d+$", suffix)
75+
end
76+
77+
--- Returns the newest installed release of a major, or nil: any release of it
78+
--- (21.0.12.1+1-tem for 21), the first one too (27+36-librca, 27-oracle for
79+
--- 27), but no EA build (26-ea+13 for 26).
80+
function installed_version.major(installed, major, suffix)
81+
local name = major .. suffix
82+
return installed_version.newest(installed, major, "^[.+][%d.+]*$", suffix) or installed[name] and name
83+
end
84+
6585
--- Returns the installed version that version .. suffix names, or nil. The
6686
--- input may leave out the build number PreInstall found, so, like PreInstall,
6787
--- take the newest build of a release (21.0.8-tem selects 21.0.8+9-tem) and
@@ -72,11 +92,10 @@ function installed_version.find(installed, version, suffix)
7292
if installed[name] then
7393
return name
7494
end
75-
-- A bare major takes any release of it (21.0.12.1+1 for 21) but no EA build
76-
-- (26-ea+13 for 26); other versions take only a build number (not
77-
-- 21.0.12.1+1 for 21.0.12, nor 21.0.12+8 for 21.0.1).
78-
local rest = version:match("^%d+$") and "^[.+][%d.+]*$" or "^%+%d+$"
79-
return installed_version.newest(installed, version, rest, suffix)
95+
if version:match("^%d+$") then
96+
return installed_version.major(installed, version, suffix)
97+
end
98+
return installed_version.release(installed, version, suffix)
8099
end
81100

82101
return installed_version

‎tests/e2e/common/common.go‎

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -273,19 +273,39 @@ func Major(version string) string {
273273
return strings.SplitN(version, ".", 2)[0]
274274
}
275275

276+
// sdkmanName returns SDKMAN's name for the distribution, which calls
277+
// JetBrains "jbr".
278+
func sdkmanName(distribution string) string {
279+
if name := Base(distribution); name != "jb" {
280+
return name
281+
}
282+
return "jbr"
283+
}
284+
276285
// LegacyIdentifier renders the SDKMAN identifier a .sdkmanrc holds for a
277286
// release: "<version>-<distribution>", without the build number, in SDKMAN's
278287
// spelling: JetBrains is "jbr" and JavaFX builds are
279288
// "<version>.fx-<distribution>" (21.0.5-jbr, 25.0.4.fx-librca).
280289
func LegacyIdentifier(distribution, version string) string {
281-
name := Base(distribution)
282-
if name == "jb" {
283-
name = "jbr"
284-
}
285290
if JavaFX(distribution) {
286291
version += ".fx"
287292
}
288-
return version + "-" + name
293+
return version + "-" + sdkmanName(distribution)
294+
}
295+
296+
// PaddedIdentifier renders the SDKMAN identifier of an installed version
297+
// with a zero padding the release, as `sdk list java` shows many
298+
// (25.0.1.0+8-open, 21.0.9.0+116386-jbr):
299+
// "<version>.0[-fx][+<build>]-<distribution>" with the installed build
300+
// (21.0.8.0+9-tem, 25.0.4.0-fx+9-librca). Foojay, and so the installed
301+
// name, leaves the zero out; ParseLegacyFile did not, so it found no such
302+
// release and fell back to the newest one of the major (vfox-java#80).
303+
func PaddedIdentifier(distribution, version, installed string) string {
304+
version += ".0"
305+
if JavaFX(distribution) {
306+
version += "-fx"
307+
}
308+
return version + buildNumber.FindString(installed) + "-" + sdkmanName(distribution)
289309
}
290310

291311
// Alias renders the asdf-style spelling "<long-name>-<version>[-fx]" of an

‎tests/e2e/common/common_test.go‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,20 @@ func TestSpecs(t *testing.T) {
208208
}
209209
}
210210

211+
func TestPaddedIdentifier(t *testing.T) {
212+
for _, c := range []struct{ dist, version, installed, want string }{
213+
{"open", "25.0.2", "25.0.2+10", "25.0.2.0+10-open"},
214+
{"tem", "21.0.8", "21.0.8+9-tem", "21.0.8.0+9-tem"},
215+
{"librca-fx", "25.0.4", "25.0.4+9-librca-fx", "25.0.4.0-fx+9-librca"},
216+
{"jb", "21.0.5", "21.0.5+79248-jb", "21.0.5.0+79248-jbr"},
217+
{"graalce", "25.0.2", "25.0.2-graalce", "25.0.2.0-graalce"},
218+
} {
219+
if got := PaddedIdentifier(c.dist, c.version, c.installed); got != c.want {
220+
t.Errorf("PaddedIdentifier(%s, %s, %s) = %q, want %q", c.dist, c.version, c.installed, got, c.want)
221+
}
222+
}
223+
}
224+
211225
func TestAlias(t *testing.T) {
212226
for _, c := range []struct{ dist, installed, want string }{
213227
{"open", "25.0.2+10", "openjdk-25.0.2+10"},

‎tests/e2e/linux/verify.go‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,11 @@ func verifySearch(ctx context.Context, t *testing.T, ctr tc.Container, distribut
5353
// verifyLegacyFile proves ParseLegacyFile selects the installed JDK from a
5454
// .sdkmanrc (PLUGIN.legacyFilenames) holding the SDKMAN identifier without
5555
// the build number (e.g. java=21.0.8-tem, java=21.0.5-jbr,
56-
// java=25.0.4.fx-librca) or the installed version itself (e.g.
57-
// java=21.0.8+9-tem), while vfox lists an empty install of another release
58-
// first (see common.Decoy). It runs before any `vfox use`, so java can only
59-
// come from the legacy file.
56+
// java=25.0.4.fx-librca), the one with a zero padding the release (e.g.
57+
// java=21.0.8.0+9-tem, see common.PaddedIdentifier) or the installed version
58+
// itself (e.g. java=21.0.8+9-tem), while vfox lists an empty install of
59+
// another release first (see common.Decoy). It runs before any `vfox use`,
60+
// so java can only come from the legacy file.
6061
func verifyLegacyFile(ctx context.Context, t *testing.T, ctr tc.Container, distribution, version, installed, arch string) {
6162
t.Helper()
6263
const dir = "/tmp/e2e-legacy-sdkmanrc"
@@ -69,7 +70,11 @@ func verifyLegacyFile(ctx context.Context, t *testing.T, ctr tc.Container, distr
6970
listed := common.ListedVersions(execOK(ctx, t, ctr, "vfox list java"))
7071
require.Equalf(t, []string{decoy, installed}, listed, "vfox list java must list the decoy %s first", decoy)
7172

72-
for _, identifier := range []string{common.LegacyIdentifier(distribution, version), installed} {
73+
for _, identifier := range []string{
74+
common.LegacyIdentifier(distribution, version),
75+
common.PaddedIdentifier(distribution, version, installed),
76+
installed,
77+
} {
7378
execOK(ctx, t, ctr, fmt.Sprintf("mkdir --parents %s && printf 'java=%%s\\n' %s > %s/.sdkmanrc",
7479
dir, shQuote(identifier), dir))
7580
// The probe activates inside its own exec, a fresh shell session, so no

‎tests/e2e/windows/verify.go‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,10 @@ func verifySearch(ctx context.Context, t *testing.T, ctr tc.Container, box slotB
5555
// verifyLegacyFile proves ParseLegacyFile selects the installed JDK from a
5656
// .sdkmanrc (PLUGIN.legacyFilenames) holding the SDKMAN identifier without
5757
// the build number (e.g. java=21.0.8-tem, java=21.0.5-jbr,
58-
// java=25.0.4.fx-librca) or the installed version itself (e.g.
59-
// java=21.0.8+9-tem), written by Set-Content with Windows line endings,
60-
// while vfox lists an empty install of another release first (see
58+
// java=25.0.4.fx-librca), the one with a zero padding the release (e.g.
59+
// java=21.0.8.0+9-tem, see common.PaddedIdentifier) or the installed version
60+
// itself (e.g. java=21.0.8+9-tem), written by Set-Content with Windows line
61+
// endings, while vfox lists an empty install of another release first (see
6162
// common.Decoy). It runs before any `vfox use`, so java can only come from
6263
// the legacy file.
6364
func verifyLegacyFile(ctx context.Context, t *testing.T, ctr tc.Container, box slotBox, distribution, version, installed, arch string) {
@@ -73,7 +74,11 @@ func verifyLegacyFile(ctx context.Context, t *testing.T, ctr tc.Container, box s
7374
listed := common.ListedVersions(execOK(ctx, t, ctr, "vfox list java", box.vars...))
7475
require.Equalf(t, []string{decoy, installed}, listed, "vfox list java must list the decoy %s first", decoy)
7576

76-
for _, identifier := range []string{common.LegacyIdentifier(distribution, version), installed} {
77+
for _, identifier := range []string{
78+
common.LegacyIdentifier(distribution, version),
79+
common.PaddedIdentifier(distribution, version, installed),
80+
installed,
81+
} {
7782
execOK(ctx, t, ctr, fmt.Sprintf(
7883
"New-Item -ItemType Directory -Force -Path %s | Out-Null; Set-Content -Path %s -Value %s",
7984
pwshQuote(dir), pwshQuote(dir+`\.sdkmanrc`), pwshQuote("java="+identifier)), box.vars...)

‎tests/parse_legacy_file_test.lua‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,26 @@ local cases = {
7878
{ "java=21.0.12-zulu\n", jb_and_zulu_fx, nil },
7979
{ "java=21.0.12.fx-zulu\n", { "21.0.12+7-zulu" }, nil },
8080
{ "java=21.0.8.fx-zulu\n", { "21.0.12+7-zulu", "21.0.12+7-zulu-fx" }, "21.0.12+7-zulu-fx" },
81+
-- SDKMAN pads releases with zeros that Foojay, and so the installed name,
82+
-- leaves out (vfox-java#80).
83+
{ "java=27.0.0+36-librca\n", { "27+36-librca" }, "27+36-librca" },
84+
{ "java=27.0.0-fx+36-librca\n", { "27+36-librca", "27+36-librca-fx" }, "27+36-librca-fx" },
85+
{ "java=27.0.0+35-zulu\n", { "27+35-zulu" }, "27+35-zulu" },
86+
{ "java=27.0.0.0+35-tem\n", { "27+35-tem" }, "27+35-tem" },
87+
{ "java=27.0.0+35-open\n", { "27+35" }, "27+35" },
88+
{ "java=26.0.0.0+35-open\n", { "26+35" }, "26+35" },
89+
{ "java=25.0.1.0+8-open\n", { "25.0.1+8", "25.0.2+10" }, "25.0.1+8" },
90+
{ "java=21.0.0.0-graal\n", { "21-graal", "21.0.12-graal" }, "21-graal" },
91+
{ "java=21.0.12.0-sapmchn\n", { "21.0.12-sapmchn", "21.0.12.1-sapmchn" }, "21.0.12-sapmchn" },
92+
{ "java=27.0.0.0+33-amzn\n", { "27-amzn" }, "27-amzn" },
93+
{ "java=21.0.10-tem\n", { "21.0.1+12-tem", "21.0.10+7-tem" }, "21.0.10+7-tem" },
94+
-- A padded major names its first release, a bare major the newest one.
95+
{ "java=26.0.0.0+35-open\n", { "26+34", "26.0.2+10" }, "26+34" },
96+
{ "java=26-open\n", { "26+34", "26.0.2+10" }, "26.0.2+10" },
97+
-- Without the release, the first release of the major is the newest one too.
98+
{ "java=25.0.1.0+8-open\n", { "25+36" }, "25+36" },
99+
{ "java=26.0.1-oracle\n", { "26-oracle" }, "26-oracle" },
100+
{ "java=26.0.2-oracle\n", { "26-oracle", "26.0.1-oracle" }, "26.0.1-oracle" },
81101
-- No java line at all selects nothing.
82102
{ "maven=3.9.9\n", { "21.0.8+9-tem" }, nil },
83103
}

0 commit comments

Comments
 (0)