From 8d31e4db55fc46f325c544d0775ca70ed59e9b45 Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Wed, 19 Oct 2022 21:10:26 +0100 Subject: [PATCH] Allow version-type: strict + otp-version: master for Elixir -based builds (#144) * Check tests failing for something that should pass * Test with version-type: strict * Add more tests GitHub actions is refusing to run a given workflow :shrug * Try to fix new test that shouldn't be failing Which is exactly what we intended to originate by starting a new pull request. * Fix tests as per CI * Fix tests as per CI * Adapt to "latest" * Fix as per CI results Actions' window shows everything as Ok but then internals not Error when evaluating 'runs-on' for job 'integration_test'. .github/workflows/ubuntu.yml (Line: 19, Col: 14): Unexpected type of value '', expected type: OneOf. Strange one, this one! * Try to "help" for non-OTP declared input We do this for OTP-, but not for maint- as the former is hopefully more common than the latter * Introduce `latest` to signify `master` Semantically they're similar, but with `latest` we know: 1. to fetch Elixir's no-otp-... version 2. to fetch Erlang's master whereas with 25 (latest at this moment) we'd try to fetch elixirvsn-otp-25 which could fail with Elixir master and version-type strict * Fix as per CI Still not 100% convinced that `latest` should be separate from `master` but I want to know how tests run, in this case in particular * Fix as per CI * Fix as per CI * Revert potential wrong decision * Fix as per understand recent Elixir changes (namely master v. main) * Try to simplify it We introduce the concept of version v. branch (as per @ericmj) and we use that to find the most appropriate Erlang/Elixir combo. --- .github/workflows/ubuntu.yml | 10 +++- __tests__/setup-beam.test.js | 12 +++- dist/index.js | 111 ++++++++++++++++------------------- src/setup-beam.js | 111 ++++++++++++++++------------------- 4 files changed, 117 insertions(+), 127 deletions(-) diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index f2fc4244..60e1b6b1 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -21,6 +21,13 @@ jobs: fail-fast: false matrix: combo: + - otp-version: 'master' + elixir-version: '1.14.0' + os: 'ubuntu-latest' + - otp-version: 'master' + elixir-version: '1.14.0' + os: 'ubuntu-latest' + version-type: 'strict' - otp-version: '25' elixir-version: '1' rebar3-version: '3' @@ -113,12 +120,13 @@ jobs: - elixir-version: 'master' otp-version: '23.1' os: 'ubuntu-20.04' - - elixir-version: 'master' + - elixir-version: 'main' otp-version: '25' os: 'ubuntu-20.04' version-type: 'strict' - gleam-version: 'nightly' otp-version: '24' + os: 'ubuntu-latest' - gleam-version: '0.23' otp-version: '24' os: 'ubuntu-latest' diff --git a/__tests__/setup-beam.test.js b/__tests__/setup-beam.test.js index 8848a25c..a4d118f1 100644 --- a/__tests__/setup-beam.test.js +++ b/__tests__/setup-beam.test.js @@ -188,6 +188,14 @@ async function testElixirVersions() { got = await setupBeam.getElixirVersion(spec, otpVersion) assert.deepStrictEqual(got, expected) + simulateInput('version-type', 'strict') + spec = '1.14.0' + otpVersion = 'master' + expected = 'v1.14.0' + got = await setupBeam.getElixirVersion(spec, otpVersion) + assert.deepStrictEqual(got, expected) + simulateInput('version-type', 'loose') + simulateInput('version-type', 'strict') spec = 'v1.11.0-rc.0' otpVersion = 'OTP-23' @@ -205,9 +213,9 @@ async function testElixirVersions() { simulateInput('version-type', 'loose') simulateInput('version-type', 'strict') - spec = 'master' + spec = 'main' otpVersion = '23.1' - expected = 'master-otp-23' + expected = 'main-otp-23' got = await setupBeam.getElixirVersion(spec, otpVersion) assert.deepStrictEqual(got, expected) simulateInput('version-type', 'loose') diff --git a/dist/index.js b/dist/index.js index e1cf030c..9bcb6310 100644 --- a/dist/index.js +++ b/dist/index.js @@ -7189,8 +7189,8 @@ async function main() { const rebar3Spec = core.getInput('rebar3-version', { required: false }) if (otpSpec !== 'false') { - const otpVersion = await installOTP(otpSpec, osVersion) - const elixirInstalled = await maybeInstallElixir(elixirSpec, otpVersion) + await installOTP(otpSpec, osVersion) + const elixirInstalled = await maybeInstallElixir(elixirSpec, otpSpec) if (elixirInstalled === true) { const shouldMixRebar = core.getInput('install-rebar', { @@ -7300,19 +7300,13 @@ async function maybeInstallRebar3(rebar3Spec) { async function getOTPVersion(otpSpec0, osVersion) { const otpVersions = await getOTPVersions(osVersion) - const otpSpec = otpSpec0.match(/^(OTP-|maint-)?([^ ]+)/) - let otpVersion - if (otpSpec[1] && !isStrictVersion()) { - throw new Error( - `Requested Erlang/OTP version (${otpSpec0}) ` + - "should not contain 'OTP-, or maint-'", - ) - } - if (otpSpec) { - otpVersion = getVersionFromSpec( - otpSpec[2], - Array.from(otpVersions.keys()).sort(), - ) + let otpSpec = otpSpec0 // might be a branch (?) + const otpVersion = getVersionFromSpec( + otpSpec, + Array.from(otpVersions.keys()).sort(), + ) + if (isVersion(otpSpec0)) { + otpSpec = `OTP-${otpSpec0}` // ... it's a version! } if (otpVersion === null) { throw new Error( @@ -7324,60 +7318,46 @@ async function getOTPVersion(otpSpec0, osVersion) { return otpVersions.get(otpVersion) // from the reference, for download } -async function getElixirVersion(exSpec0, otpVersion) { +async function getElixirVersion(exSpec0, otpVersion0) { + const otpVersion = otpVersion0.match(/^([^-]+-)?(.+)$/)[2] + const otpVersionMajor = otpVersion.match(/^([^.]+).*$/)[1] const elixirVersions = await getElixirVersions() const semverVersions = Array.from(elixirVersions.keys()).sort() - - const exSpec = exSpec0.match(/^v?(.+)(-otp-.+)/) || exSpec0.match(/^v?(.+)/) - let elixirVersion - if (exSpec[2] && !isStrictVersion()) { - throw new Error( - `Requested Elixir / Erlang/OTP version (${exSpec0} / ${otpVersion}) ` + - "should not contain '-otp-...'", - ) + const exSpec = exSpec0.replace(/-otp-.*$/, '') + const elixirVersionFromSpec = getVersionFromSpec(exSpec, semverVersions, true) + let elixirVersionForDownload = elixirVersionFromSpec + if (isVersion(otpVersionMajor)) { + elixirVersionForDownload = `${elixirVersionFromSpec}-otp-${otpVersionMajor}` } - if (exSpec) { - elixirVersion = getVersionFromSpec(exSpec[1], semverVersions) - } - if (!exSpec || elixirVersion === null) { + if (elixirVersionFromSpec === null) { throw new Error( `Requested Elixir version (${exSpec0}) not found in version list ` + "(should you be using option 'version-type': 'strict'?)", ) } - const otpMatch = otpVersion.match(/^(?:OTP-)?([^.]+)/) - let elixirVersionWithOTP - - if (elixirVersions.get(elixirVersion)) { - const otpVersionMajor = otpMatch[1] - // We try for a version like `v1.4.5-otp-20`... - if (elixirVersions.get(elixirVersion).includes(otpMatch[1])) { - // ... and it's available: use it! - elixirVersionWithOTP = `${elixirVersion}-otp-${otpVersionMajor}` - core.info( - `Using Elixir ${elixirVersion} (built for OTP ${otpVersionMajor})`, - ) - } else { - // ... and it's not available: exit with exception - throw new Error( - `Requested Elixir / Erlang/OTP version (${exSpec0} / ${otpVersion}) not ` + - 'found in version list (did you check Compatibility between Elixir and Erlang/OTP?)', - ) - } + + const elixirVersionComp = elixirVersions.get(elixirVersionFromSpec) + if ( + (elixirVersionComp && elixirVersionComp.includes(otpVersionMajor)) || + !isVersion(otpVersionMajor) + ) { + core.info( + `Using Elixir ${elixirVersionFromSpec} (built for Erlang/OTP ${otpVersionMajor})`, + ) } else { throw new Error( - `Requested Elixir / Erlang/OTP version (${exSpec0} / ${otpVersion}) not ` + - "found in version list (should you be using option 'version-type': 'strict'?)", + `Requested Elixir / Erlang/OTP version (${exSpec0} / ${otpVersion0}) not ` + + 'found in version list (did you check Compatibility between Elixir and Erlang/OTP?)', ) } - return maybePrependWithV(elixirVersionWithOTP, elixirVersion) + return maybePrependWithV(elixirVersionForDownload) } async function getGleamVersion(gleamSpec0) { - const gleamSpec = gleamSpec0.match(/^v?(.+)/) + const gleamSpec = gleamSpec0.match(/^v?(.+)$/) const gleamVersions = await getGleamVersions() - const gleamVersion = getVersionFromSpec(gleamSpec[1], gleamVersions) + const gleamVersion = getVersionFromSpec(gleamSpec[1], gleamVersions, true) if (gleamVersion === null) { throw new Error( `Requested Gleam version (${gleamSpec0}) not found in version list ` + @@ -7421,7 +7401,9 @@ async function getOTPVersions(osVersion) { .trim() .split('\n') .forEach((line) => { - const otpMatch = line.match(/^(OTP-|maint-)?([^ ]+)/) + const otpMatch = line + .match(/^([^ ]+)?( .+)/)[1] + .match(/^([^-]+-)?(.+)$/) const otpVersion = otpMatch[2] otpVersions.set(otpVersion, otpMatch[0]) // we keep the original for later reference }) @@ -7438,7 +7420,6 @@ async function getOTPVersions(osVersion) { }) }) } - return otpVersions } @@ -7455,7 +7436,7 @@ async function getElixirVersions() { .forEach((line) => { const elixirMatch = line.match(/^v?(.+)-otp-([^ ]+)/) || line.match(/^v?([^ ]+)/) - const elixirVersion = elixirMatch[1] + const elixirVersion = maybePrependWithV(elixirMatch[1]) const otpVersion = elixirMatch[2] const otpVersions = otpVersionsForElixirMap.get(elixirVersion) || [] if (otpVersion) { @@ -7502,7 +7483,7 @@ function isStrictVersion() { return core.getInput('version-type', { required: false }) === 'strict' } -function getVersionFromSpec(spec, versions) { +function getVersionFromSpec(spec, versions, maybePrependWithV0) { let version = null if (spec.match(/rc/) || isStrictVersion()) { @@ -7527,7 +7508,11 @@ function getVersionFromSpec(spec, versions) { } } - return version === null || version === undefined ? null : version + let v = version === null || version === undefined ? null : version + if (maybePrependWithV0 && v != null) { + v = maybePrependWithV(v) + } + return v } function maybeCoerced(v) { @@ -7634,15 +7619,17 @@ async function get(url0, pageIdxs) { return ret } -function maybePrependWithV(versionToPrepend, specVersion) { - const digitStart = /^\d+/ - let v = versionToPrepend - if (digitStart.test(specVersion)) { - v = `v${versionToPrepend}` +function maybePrependWithV(v) { + if (isVersion(v)) { + return `v${v.replace('v', '')}` } return v } +function isVersion(v) { + return /^v?\d+/.test(v) +} + module.exports = { getOTPVersion, getElixirVersion, diff --git a/src/setup-beam.js b/src/setup-beam.js index 26452f04..8e614b5b 100644 --- a/src/setup-beam.js +++ b/src/setup-beam.js @@ -19,8 +19,8 @@ async function main() { const rebar3Spec = core.getInput('rebar3-version', { required: false }) if (otpSpec !== 'false') { - const otpVersion = await installOTP(otpSpec, osVersion) - const elixirInstalled = await maybeInstallElixir(elixirSpec, otpVersion) + await installOTP(otpSpec, osVersion) + const elixirInstalled = await maybeInstallElixir(elixirSpec, otpSpec) if (elixirInstalled === true) { const shouldMixRebar = core.getInput('install-rebar', { @@ -130,19 +130,13 @@ async function maybeInstallRebar3(rebar3Spec) { async function getOTPVersion(otpSpec0, osVersion) { const otpVersions = await getOTPVersions(osVersion) - const otpSpec = otpSpec0.match(/^(OTP-|maint-)?([^ ]+)/) - let otpVersion - if (otpSpec[1] && !isStrictVersion()) { - throw new Error( - `Requested Erlang/OTP version (${otpSpec0}) ` + - "should not contain 'OTP-, or maint-'", - ) - } - if (otpSpec) { - otpVersion = getVersionFromSpec( - otpSpec[2], - Array.from(otpVersions.keys()).sort(), - ) + let otpSpec = otpSpec0 // might be a branch (?) + const otpVersion = getVersionFromSpec( + otpSpec, + Array.from(otpVersions.keys()).sort(), + ) + if (isVersion(otpSpec0)) { + otpSpec = `OTP-${otpSpec0}` // ... it's a version! } if (otpVersion === null) { throw new Error( @@ -154,60 +148,46 @@ async function getOTPVersion(otpSpec0, osVersion) { return otpVersions.get(otpVersion) // from the reference, for download } -async function getElixirVersion(exSpec0, otpVersion) { +async function getElixirVersion(exSpec0, otpVersion0) { + const otpVersion = otpVersion0.match(/^([^-]+-)?(.+)$/)[2] + const otpVersionMajor = otpVersion.match(/^([^.]+).*$/)[1] const elixirVersions = await getElixirVersions() const semverVersions = Array.from(elixirVersions.keys()).sort() - - const exSpec = exSpec0.match(/^v?(.+)(-otp-.+)/) || exSpec0.match(/^v?(.+)/) - let elixirVersion - if (exSpec[2] && !isStrictVersion()) { - throw new Error( - `Requested Elixir / Erlang/OTP version (${exSpec0} / ${otpVersion}) ` + - "should not contain '-otp-...'", - ) + const exSpec = exSpec0.replace(/-otp-.*$/, '') + const elixirVersionFromSpec = getVersionFromSpec(exSpec, semverVersions, true) + let elixirVersionForDownload = elixirVersionFromSpec + if (isVersion(otpVersionMajor)) { + elixirVersionForDownload = `${elixirVersionFromSpec}-otp-${otpVersionMajor}` } - if (exSpec) { - elixirVersion = getVersionFromSpec(exSpec[1], semverVersions) - } - if (!exSpec || elixirVersion === null) { + if (elixirVersionFromSpec === null) { throw new Error( `Requested Elixir version (${exSpec0}) not found in version list ` + "(should you be using option 'version-type': 'strict'?)", ) } - const otpMatch = otpVersion.match(/^(?:OTP-)?([^.]+)/) - let elixirVersionWithOTP - - if (elixirVersions.get(elixirVersion)) { - const otpVersionMajor = otpMatch[1] - // We try for a version like `v1.4.5-otp-20`... - if (elixirVersions.get(elixirVersion).includes(otpMatch[1])) { - // ... and it's available: use it! - elixirVersionWithOTP = `${elixirVersion}-otp-${otpVersionMajor}` - core.info( - `Using Elixir ${elixirVersion} (built for OTP ${otpVersionMajor})`, - ) - } else { - // ... and it's not available: exit with exception - throw new Error( - `Requested Elixir / Erlang/OTP version (${exSpec0} / ${otpVersion}) not ` + - 'found in version list (did you check Compatibility between Elixir and Erlang/OTP?)', - ) - } + + const elixirVersionComp = elixirVersions.get(elixirVersionFromSpec) + if ( + (elixirVersionComp && elixirVersionComp.includes(otpVersionMajor)) || + !isVersion(otpVersionMajor) + ) { + core.info( + `Using Elixir ${elixirVersionFromSpec} (built for Erlang/OTP ${otpVersionMajor})`, + ) } else { throw new Error( - `Requested Elixir / Erlang/OTP version (${exSpec0} / ${otpVersion}) not ` + - "found in version list (should you be using option 'version-type': 'strict'?)", + `Requested Elixir / Erlang/OTP version (${exSpec0} / ${otpVersion0}) not ` + + 'found in version list (did you check Compatibility between Elixir and Erlang/OTP?)', ) } - return maybePrependWithV(elixirVersionWithOTP, elixirVersion) + return maybePrependWithV(elixirVersionForDownload) } async function getGleamVersion(gleamSpec0) { - const gleamSpec = gleamSpec0.match(/^v?(.+)/) + const gleamSpec = gleamSpec0.match(/^v?(.+)$/) const gleamVersions = await getGleamVersions() - const gleamVersion = getVersionFromSpec(gleamSpec[1], gleamVersions) + const gleamVersion = getVersionFromSpec(gleamSpec[1], gleamVersions, true) if (gleamVersion === null) { throw new Error( `Requested Gleam version (${gleamSpec0}) not found in version list ` + @@ -251,7 +231,9 @@ async function getOTPVersions(osVersion) { .trim() .split('\n') .forEach((line) => { - const otpMatch = line.match(/^(OTP-|maint-)?([^ ]+)/) + const otpMatch = line + .match(/^([^ ]+)?( .+)/)[1] + .match(/^([^-]+-)?(.+)$/) const otpVersion = otpMatch[2] otpVersions.set(otpVersion, otpMatch[0]) // we keep the original for later reference }) @@ -268,7 +250,6 @@ async function getOTPVersions(osVersion) { }) }) } - return otpVersions } @@ -285,7 +266,7 @@ async function getElixirVersions() { .forEach((line) => { const elixirMatch = line.match(/^v?(.+)-otp-([^ ]+)/) || line.match(/^v?([^ ]+)/) - const elixirVersion = elixirMatch[1] + const elixirVersion = maybePrependWithV(elixirMatch[1]) const otpVersion = elixirMatch[2] const otpVersions = otpVersionsForElixirMap.get(elixirVersion) || [] if (otpVersion) { @@ -332,7 +313,7 @@ function isStrictVersion() { return core.getInput('version-type', { required: false }) === 'strict' } -function getVersionFromSpec(spec, versions) { +function getVersionFromSpec(spec, versions, maybePrependWithV0) { let version = null if (spec.match(/rc/) || isStrictVersion()) { @@ -357,7 +338,11 @@ function getVersionFromSpec(spec, versions) { } } - return version === null || version === undefined ? null : version + let v = version === null || version === undefined ? null : version + if (maybePrependWithV0 && v != null) { + v = maybePrependWithV(v) + } + return v } function maybeCoerced(v) { @@ -464,15 +449,17 @@ async function get(url0, pageIdxs) { return ret } -function maybePrependWithV(versionToPrepend, specVersion) { - const digitStart = /^\d+/ - let v = versionToPrepend - if (digitStart.test(specVersion)) { - v = `v${versionToPrepend}` +function maybePrependWithV(v) { + if (isVersion(v)) { + return `v${v.replace('v', '')}` } return v } +function isVersion(v) { + return /^v?\d+/.test(v) +} + module.exports = { getOTPVersion, getElixirVersion,