Fix: more versioning around `nightly` and `maint`/`main` (#359)

fceaea9 · Paulo F. Oliveira · 2025-07-10 15:20

10 files +119 -91
Message
{commit_body(@commit)}

Files changed

modified .github/workflows/macos.yml
+0 −6
@@ -43,12 +43,6 @@ jobs:
43 43 elixir-version: '1.14.3'
44 44 os: 'macos-latest'
45 45 version-type: 'strict'
46 - otp-version: '27'
47 os: 'macos-15'
48 version-type: 'strict'
49 - otp-version: '26'
50 os: 'macos-15'
51 version-type: 'strict'
52 46 - otp-version: '25'
53 47 os: 'macos-15'
54 48 - otp-version: '26'
modified .github/workflows/ubuntu.yml
+0 −6
@@ -53,12 +53,6 @@ jobs:
53 53 elixir-version: '1.14.3'
54 54 os: 'ubuntu-latest'
55 55 version-type: 'strict'
56 - otp-version: '27'
57 os: 'ubuntu-24.04'
58 version-type: 'strict'
59 - otp-version: '26'
60 os: 'ubuntu-24.04'
61 version-type: 'strict'
62 56 - otp-version: '25'
63 57 os: 'ubuntu-24.04'
64 58 - otp-version: '26'
modified dist/index.js
+22 −20
@@ -26045,10 +26045,14 @@ async function maybeInstallRebar3(rebar3Spec) {
26045 26045 return installed
26046 26046 }
26047 26047
26048 +function maybeRemoveOTPPrefix(otpSpec) {
26049 + return otpSpec.replace(/^OTP-/, '')
26050 +}
26051 +
26048 26052 async function getOTPVersion(otpSpec0, osVersion) {
26049 26053 const [otpVersions, originListing, hexMirrors] =
26050 26054 await getOTPVersions(osVersion)
26051 let spec = otpSpec0.replace(/^OTP-/, '')
26055 + let spec = maybeRemoveOTPPrefix(otpSpec0)
26052 26056 const versions = otpVersions
26053 26057 const otpVersion = getVersionFromSpec(spec, versions)
26054 26058
@@ -26224,12 +26228,9 @@ async function getOTPVersions(osVersion) {
26224 26228 .trim()
26225 26229 .split('\n')
26226 26230 .forEach((line) => {
26227 const otpMatch = line
26228 .match(/^([^ ]+)?( .+)/)[1]
26229 .match(/^([^-]+-)?(.+)$/)
26230 const otpVersion = otpMatch[2]
26231 const otpVersionOrig = otpMatch[0]
26232 debugLog('OTP line and parsing', [line, otpVersion, otpMatch])
26231 + const otpVersionOrig = line.match(/^([^ ]+)?( .+)/)[1]
26232 + const otpVersion = maybeRemoveOTPPrefix(otpVersionOrig)
26233 + debugLog('OTP line and parsing', [line, otpVersion, otpVersionOrig])
26233 26234 otpVersions[otpVersion] = otpVersionOrig // we keep the original for later reference
26234 26235 })
26235 26236 } else if (process.platform === 'win32') {
@@ -26243,10 +26244,10 @@ async function getOTPVersions(osVersion) {
26243 26244 .flat()
26244 26245 .filter((x) => x.name.match(file_regex))
26245 26246 .forEach((x) => {
26246 const otpMatch = x.name.match(file_regex)
26247 const otpVersion = otpMatch[1]
26248 debugLog('OTP line and parsing', [otpMatch, otpVersion])
26249 otpVersions[otpVersion] = otpVersion
26247 + const otpVersionOrig = x.name.match(file_regex)[1]
26248 + const otpVersion = otpVersionOrig
26249 + debugLog('OTP line and parsing', [x.name, otpVersion, otpVersionOrig])
26250 + otpVersions[otpVersion] = otpVersionOrig
26250 26251 })
26251 26252 })
26252 26253 } else if (process.platform === 'darwin') {
@@ -26255,10 +26256,9 @@ async function getOTPVersions(osVersion) {
26255 26256 columns: true,
26256 26257 })
26257 26258 .forEach((line) => {
26258 const otpMatch = line.ref_name.match(/^([^-]+-)?(.+)$/)
26259 const otpVersion = otpMatch[2]
26260 const otpVersionOrig = otpMatch[0]
26261 debugLog('OTP line and parsing', [line, otpVersion, otpMatch])
26259 + const otpVersionOrig = line.ref_name
26260 + const otpVersion = maybeRemoveOTPPrefix(otpVersionOrig)
26261 + debugLog('OTP line and parsing', [line, otpVersion, otpVersionOrig])
26262 26262 otpVersions[otpVersion] = otpVersionOrig // we keep the original for later reference
26263 26263 })
26264 26264 }
@@ -26384,7 +26384,8 @@ function getVersionFromSpec(spec0, versions0) {
26384 26384 isKnownBranch(version) ||
26385 26385 isKnownVerBranch(version)
26386 26386 ) {
26387 // If `version-type: strict` or version is RC, we just try to remove a potential initial v
26387 + // If `version-type: strict`, version is an RC, or version is "a branch"
26388 + // we just try to remove a potential initial v
26388 26389 coerced = maybeRemoveVPrefix(version)
26389 26390 } else {
26390 26391 // Otherwise, we place the version into a version bucket
@@ -26401,18 +26402,19 @@ function getVersionFromSpec(spec0, versions0) {
26401 26402 const rangeMax = semver.maxSatisfying(versions, rangeForMax)
26402 26403 let version = null
26403 26404
26404 if (
26405 + if (spec0 === 'latest') {
26406 + version = versions0[versions0.latest]
26407 + } else if (
26405 26408 isStrictVersion() ||
26406 26409 isRC(spec0) ||
26407 26410 isKnownBranch(spec0) ||
26408 isKnownVerBranch(spec0)
26411 + isKnownVerBranch(spec0) ||
26412 + spec0 === 'nightly'
26409 26413 ) {
26410 26414 if (versions0[spec]) {
26411 26415 // We obtain it directly
26412 26416 version = versions0[spec]
26413 26417 }
26414 } else if (spec0 === 'latest') {
26415 version = versions0[versions0.latest]
26416 26418 } else if (rangeMax !== null) {
26417 26419 // Otherwise, we compare alt. versions' semver ranges to this version, from highest to lowest
26418 26420 const thatVersion = spec
modified src/setup-beam.js
+22 −20
@@ -170,10 +170,14 @@ async function maybeInstallRebar3(rebar3Spec) {
170 170 return installed
171 171 }
172 172
173 +function maybeRemoveOTPPrefix(otpSpec) {
174 + return otpSpec.replace(/^OTP-/, '')
175 +}
176 +
173 177 async function getOTPVersion(otpSpec0, osVersion) {
174 178 const [otpVersions, originListing, hexMirrors] =
175 179 await getOTPVersions(osVersion)
176 let spec = otpSpec0.replace(/^OTP-/, '')
180 + let spec = maybeRemoveOTPPrefix(otpSpec0)
177 181 const versions = otpVersions
178 182 const otpVersion = getVersionFromSpec(spec, versions)
179 183
@@ -349,12 +353,9 @@ async function getOTPVersions(osVersion) {
349 353 .trim()
350 354 .split('\n')
351 355 .forEach((line) => {
352 const otpMatch = line
353 .match(/^([^ ]+)?( .+)/)[1]
354 .match(/^([^-]+-)?(.+)$/)
355 const otpVersion = otpMatch[2]
356 const otpVersionOrig = otpMatch[0]
357 debugLog('OTP line and parsing', [line, otpVersion, otpMatch])
356 + const otpVersionOrig = line.match(/^([^ ]+)?( .+)/)[1]
357 + const otpVersion = maybeRemoveOTPPrefix(otpVersionOrig)
358 + debugLog('OTP line and parsing', [line, otpVersion, otpVersionOrig])
358 359 otpVersions[otpVersion] = otpVersionOrig // we keep the original for later reference
359 360 })
360 361 } else if (process.platform === 'win32') {
@@ -368,10 +369,10 @@ async function getOTPVersions(osVersion) {
368 369 .flat()
369 370 .filter((x) => x.name.match(file_regex))
370 371 .forEach((x) => {
371 const otpMatch = x.name.match(file_regex)
372 const otpVersion = otpMatch[1]
373 debugLog('OTP line and parsing', [otpMatch, otpVersion])
374 otpVersions[otpVersion] = otpVersion
372 + const otpVersionOrig = x.name.match(file_regex)[1]
373 + const otpVersion = otpVersionOrig
374 + debugLog('OTP line and parsing', [x.name, otpVersion, otpVersionOrig])
375 + otpVersions[otpVersion] = otpVersionOrig
375 376 })
376 377 })
377 378 } else if (process.platform === 'darwin') {
@@ -380,10 +381,9 @@ async function getOTPVersions(osVersion) {
380 381 columns: true,
381 382 })
382 383 .forEach((line) => {
383 const otpMatch = line.ref_name.match(/^([^-]+-)?(.+)$/)
384 const otpVersion = otpMatch[2]
385 const otpVersionOrig = otpMatch[0]
386 debugLog('OTP line and parsing', [line, otpVersion, otpMatch])
384 + const otpVersionOrig = line.ref_name
385 + const otpVersion = maybeRemoveOTPPrefix(otpVersionOrig)
386 + debugLog('OTP line and parsing', [line, otpVersion, otpVersionOrig])
387 387 otpVersions[otpVersion] = otpVersionOrig // we keep the original for later reference
388 388 })
389 389 }
@@ -509,7 +509,8 @@ function getVersionFromSpec(spec0, versions0) {
509 509 isKnownBranch(version) ||
510 510 isKnownVerBranch(version)
511 511 ) {
512 // If `version-type: strict` or version is RC, we just try to remove a potential initial v
512 + // If `version-type: strict`, version is an RC, or version is "a branch"
513 + // we just try to remove a potential initial v
513 514 coerced = maybeRemoveVPrefix(version)
514 515 } else {
515 516 // Otherwise, we place the version into a version bucket
@@ -526,18 +527,19 @@ function getVersionFromSpec(spec0, versions0) {
526 527 const rangeMax = semver.maxSatisfying(versions, rangeForMax)
527 528 let version = null
528 529
529 if (
530 + if (spec0 === 'latest') {
531 + version = versions0[versions0.latest]
532 + } else if (
530 533 isStrictVersion() ||
531 534 isRC(spec0) ||
532 535 isKnownBranch(spec0) ||
533 isKnownVerBranch(spec0)
536 + isKnownVerBranch(spec0) ||
537 + spec0 === 'nightly'
534 538 ) {
535 539 if (versions0[spec]) {
536 540 // We obtain it directly
537 541 version = versions0[spec]
538 542 }
539 } else if (spec0 === 'latest') {
540 version = versions0[versions0.latest]
541 543 } else if (rangeMax !== null) {
542 544 // Otherwise, we compare alt. versions' semver ranges to this version, from highest to lowest
543 545 const thatVersion = spec
modified test/setup-beam.test.js
+75 −39
@@ -77,7 +77,7 @@ describe('OTP install', () => {
77 77 const otpOSVersion = 'ubuntu-08.04'
78 78 const otpVersion = 'OTP-23.2'
79 79
80 assert.rejects(
80 + await assert.rejects(
81 81 async () => {
82 82 await setupBeam.install('otp', {
83 83 hexMirror: 'https://builds.hex.pm',
@@ -97,7 +97,7 @@ describe('OTP install', () => {
97 97 describe('Elixir install', () => {
98 98 it('fails for version 0.11 without OTP', async () => {
99 99 const exVersion = '0.11'
100 assert.rejects(
100 + await assert.rejects(
101 101 async () => {
102 102 await setupBeam.install('elixir', {
103 103 hexMirror: 'https://builds.hex.pm',
@@ -114,7 +114,7 @@ describe('Elixir install', () => {
114 114
115 115 it('fails for version 1.0.0 on OTP 17 (without OTP)', async () => {
116 116 const exVersion = 'v1.0.0-otp-17'
117 assert.rejects(
117 + await assert.rejects(
118 118 async () => {
119 119 await setupBeam.install('elixir', {
120 120 hexMirror: 'https://builds.hex.pm',
@@ -133,7 +133,7 @@ describe('Elixir install', () => {
133 133 describe('Gleam install', () => {
134 134 it('fails for unknown OTP', async () => {
135 135 const gleamVersion = '0.1.3'
136 assert.rejects(
136 + await assert.rejects(
137 137 async () => {
138 138 await setupBeam.install('gleam', { gleamVersion })
139 139 },
@@ -149,7 +149,7 @@ describe('Gleam install', () => {
149 149 describe('rebar3 install', () => {
150 150 it('fails for unknown OTP', async () => {
151 151 const r3Version = '0.14.4'
152 assert.rejects(
152 + await assert.rejects(
153 153 async () => {
154 154 await setupBeam.install('rebar3', { r3Version })
155 155 },
@@ -179,14 +179,6 @@ describe('.getOTPVersion(_) - Erlang', () => {
179 179 process.env.RUNNER_ARCH = 'X64'
180 180
181 181 it('is Ok for known linux version', async () => {
182 before = simulateInput('version-type', 'strict')
183 spec = '26'
184 osVersion = 'ubuntu-24.04'
185 expected = 'maint-26'
186 got = await setupBeam.getOTPVersion(spec, osVersion)
187 assert.deepStrictEqual(got, expected)
188 simulateInput('version-type', before)
189
190 182 before = simulateInput('version-type', 'strict')
191 183 spec = '27.0'
192 184 osVersion = 'ubuntu-24.04'
@@ -311,19 +303,46 @@ describe('.getOTPVersion(_) - Erlang', () => {
311 303 }
312 304
313 305 if (process.platform === 'linux') {
314 it('is Ok for known linux ARM64 version', async () => {
306 + it('is main-... only if strict/maint-... is used as input', async () => {
315 307 const arm64Options = setupBeam.githubARMRunnerArchs()
316 308 process.env.RUNNER_ARCH =
317 309 arm64Options[Math.floor(Math.random() * arm64Options.length)]
318 310
319 311 before = simulateInput('version-type', 'strict')
320 spec = '26'
321 osVersion = 'ubuntu-24.04'
322 expected = 'maint-26'
323 got = await setupBeam.getOTPVersion(spec, osVersion)
324 assert.deepStrictEqual(got, expected)
312 + await assert.rejects(
313 + async () => {
314 + await setupBeam.getOTPVersion('27', 'ubuntu-24.04')
315 + },
316 + (err) => {
317 + assert.ok(err instanceof Error)
318 + return true
319 + },
320 + `Fetching strict OTP version maint-<v> with input <v> is supposed to fail`,
321 + )
325 322 simulateInput('version-type', before)
326 323
324 + before = simulateInput('version-type', 'strict')
325 + assert.strictEqual(
326 + await setupBeam.getOTPVersion('maint-27', 'ubuntu-24.04'),
327 + 'maint-27',
328 + )
329 + simulateInput('version-type', before)
330 +
331 + before = simulateInput('version-type', 'loose')
332 + assert.strictEqual(
333 + await setupBeam.getOTPVersion('maint-27', 'ubuntu-24.04'),
334 + 'maint-27',
335 + )
336 + simulateInput('version-type', before)
337 +
338 + process.env.RUNNER_ARCH = previousRunnerArch
339 + })
340 +
341 + it('is Ok for known linux ARM64 version', async () => {
342 + const arm64Options = setupBeam.githubARMRunnerArchs()
343 + process.env.RUNNER_ARCH =
344 + arm64Options[Math.floor(Math.random() * arm64Options.length)]
345 +
327 346 before = simulateInput('version-type', 'strict')
328 347 spec = '27.0'
329 348 osVersion = 'ubuntu-24.04'
@@ -388,14 +407,6 @@ describe('.getOTPVersion(_) - Erlang', () => {
388 407 process.env.RUNNER_ARCH =
389 408 amd64Options[Math.floor(Math.random() * amd64Options.length)]
390 409
391 before = simulateInput('version-type', 'strict')
392 spec = '26'
393 osVersion = 'ubuntu-24.04'
394 expected = 'maint-26'
395 got = await setupBeam.getOTPVersion(spec, osVersion)
396 assert.deepStrictEqual(got, expected)
397 simulateInput('version-type', before)
398
399 410 before = simulateInput('version-type', 'strict')
400 411 spec = '27.0'
401 412 osVersion = 'ubuntu-24.04'
@@ -475,6 +486,35 @@ describe('.getOTPVersion(_) - Erlang', () => {
475 486 }
476 487
477 488 if (process.platform === 'darwin') {
489 + it('is main-... only if strict/maint-... is used as input', async () => {
490 + const arm64Options = setupBeam.githubARMRunnerArchs()
491 + process.env.RUNNER_ARCH =
492 + arm64Options[Math.floor(Math.random() * arm64Options.length)]
493 +
494 + before = simulateInput('version-type', 'strict')
495 + await assert.rejects(
496 + async () => {
497 + await setupBeam.getOTPVersion('27')
498 + },
499 + (err) => {
500 + assert.ok(err instanceof Error)
501 + return true
502 + },
503 + `Fetching strict OTP version maint-<v> with input <v> is supposed to fail`,
504 + )
505 + simulateInput('version-type', before)
506 +
507 + before = simulateInput('version-type', 'strict')
508 + assert.strictEqual(await setupBeam.getOTPVersion('maint-27'), 'maint-27')
509 + simulateInput('version-type', before)
510 +
511 + before = simulateInput('version-type', 'loose')
512 + assert.strictEqual(await setupBeam.getOTPVersion('maint-27'), 'maint-27')
513 + simulateInput('version-type', before)
514 +
515 + process.env.RUNNER_ARCH = previousRunnerArch
516 + })
517 +
478 518 it('is Ok for known macos ARM64 version', async () => {
479 519 const arm64Options = setupBeam.githubARMRunnerArchs()
480 520 process.env.RUNNER_ARCH =
@@ -517,7 +557,7 @@ describe('OTP arch-specific install', () => {
517 557 const spec = '26'
518 558 const osVersion = 'ubuntu-24.04'
519 559
520 assert.rejects(
560 + await assert.rejects(
521 561 async () => {
522 562 await setupBeam.getOTPVersion(spec, osVersion)
523 563 },
@@ -583,7 +623,7 @@ describe('.getOTPVersion(_) - Elixir', () => {
583 623
584 624 before = simulateInput('version-type', 'strict')
585 625 spec = 'v1.15.0-rc.2'
586 otpVersion = 'OTP-26'
626 + otpVersion = 'OTP-26.0'
587 627 expected = 'v1.15.0-rc.2-otp-26'
588 628 await setupBeam.installOTP(otpVersion)
589 629 got = await setupBeam.getElixirVersion(spec, otpVersion)
@@ -617,6 +657,12 @@ describe('.getOTPVersion(_) - Gleam', () => {
617 657 got = await setupBeam.getGleamVersion(spec, otpVersion)
618 658 assert.deepStrictEqual(got, expected)
619 659
660 + spec = 'nightly'
661 + otpVersion = '28'
662 + expected = 'nightly'
663 + got = await setupBeam.getGleamVersion(spec, otpVersion)
664 + assert.deepStrictEqual(got, expected)
665 +
620 666 spec = '0.13.2'
621 667 otpVersion = 'OTP-24'
622 668 expected = 'v0.13.2'
@@ -870,11 +916,6 @@ describe('.getVersionFromSpec(_)', () => {
870 916 got = setupBeam.getVersionFromSpec(spec, matrix.otp['ubuntu-20.04'])
871 917 assert.deepStrictEqual(got, expected)
872 918
873 spec = 'maint-24'
874 expected = 'maint-24'
875 got = setupBeam.getVersionFromSpec(spec, matrix.otp['ubuntu-22.04'])
876 assert.deepStrictEqual(got, expected)
877
878 919 spec = '> 0'
879 920 expected = 'OTP-26.2.5'
880 921 got = setupBeam.getVersionFromSpec(spec, matrix.otp['ubuntu-22.04'])
@@ -885,11 +926,6 @@ describe('.getVersionFromSpec(_)', () => {
885 926 got = setupBeam.getVersionFromSpec(spec, matrix.otp['ubuntu-22.04'])
886 927 assert.deepStrictEqual(got, expected)
887 928
888 spec = 'maint-26'
889 expected = 'maint-26'
890 got = setupBeam.getVersionFromSpec(spec, matrix.otp['ubuntu-22.04'])
891 assert.deepStrictEqual(got, expected)
892
893 929 spec = '> 0'
894 930 expected = 'OTP-27.0'
895 931 got = setupBeam.getVersionFromSpec(spec, matrix.otp['ubuntu-24.04'])

Parents: 2bb5b65