From d8a37f5aac5697ad06bc85db417e9cd4e3b5c27d Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Thu, 8 Jul 2021 15:35:25 +0100 Subject: [PATCH] Improve on "Add Windows to the mix" (#52) * Support more OTP+Windows versions (try to bridge the gap with gleam-lang/setup-erlang that seems to support pre-21 versions) * Merge ci.yml and test.yml * Add Elixir to the Windows mix * Hopefully improve error/warning messages * Adapt the doc.s to the current reality * Add option version-type (loose by default, for compatibility) --- .github/workflows/{ci.yml => action.yml} | 14 +- .github/workflows/{test.yml => ubuntu.yml} | 62 +------- .github/workflows/windows.yml | 87 +++++++++++ README.md | 51 ++++--- __tests__/setup-beam.test.js | 169 +++++++++++++++++++-- action.yml | 10 +- dist/index.js | 143 +++++++++-------- dist/install-elixir.ps1 | 18 ++- dist/install-elixir.sh | 4 - dist/install-otp.ps1 | 7 +- dist/install-otp.sh | 4 - dist/install-rebar3.ps1 | 3 - dist/install-rebar3.sh | 3 - src/install-elixir.ps1 | 18 ++- src/install-elixir.sh | 4 - src/install-otp.ps1 | 7 +- src/install-otp.sh | 4 - src/install-rebar3.ps1 | 3 - src/install-rebar3.sh | 3 - src/installer.js | 6 +- src/setup-beam.js | 137 ++++++++++------- 21 files changed, 499 insertions(+), 258 deletions(-) rename .github/workflows/{ci.yml => action.yml} (70%) rename .github/workflows/{test.yml => ubuntu.yml} (68%) create mode 100644 .github/workflows/windows.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/action.yml similarity index 70% rename from .github/workflows/ci.yml rename to .github/workflows/action.yml index 357ed8a6..b30a3048 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/action.yml @@ -1,5 +1,5 @@ --- -name: ci +name: action on: push: @@ -11,7 +11,7 @@ on: jobs: check_integrity: - name: Make sure expected pre-release actions are performed + name: Expected local npm actions runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -30,3 +30,13 @@ jobs: - run: npm run licenses - name: Check if build left artifacts run: git diff --exit-code + + unit_test: + name: Unit tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: {node-version: '12'} + - run: npm ci + - run: npm test diff --git a/.github/workflows/test.yml b/.github/workflows/ubuntu.yml similarity index 68% rename from .github/workflows/test.yml rename to .github/workflows/ubuntu.yml index 50229d86..ef014d66 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/ubuntu.yml @@ -1,5 +1,5 @@ --- -name: test +name: ubuntu on: push: @@ -10,23 +10,11 @@ on: - main jobs: - unit_test: - name: Pre-release unit tests - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 - with: {node-version: '12'} - - run: npm ci - - run: npm test - - integration_test_ubuntu: + integration_test: name: > - Pre-release integration tests - (Ubuntu ${{matrix.combo.os}}, - Erlang/OTP ${{matrix.combo.otp-version}}, - Elixir ${{matrix.combo.elixir-version}}, - rebar3 ${{matrix.combo.rebar3-version}}) + OTP ${{matrix.combo.otp-version}}, + Elixir ${{matrix.combo.elixir-version}}, + rebar3 ${{matrix.combo.rebar3-version}} runs-on: ${{matrix.combo.os}} strategy: fail-fast: false @@ -138,43 +126,3 @@ jobs: cd test-projects/rebar3 rebar3 ct if: ${{matrix.combo.rebar3-version}} - - integration_test_windows: - name: > - Pre-release integration tests - (Windows ${{matrix.combo.os}}, - Erlang/OTP ${{matrix.combo.otp-version}}, - rebar3 ${{matrix.combo.rebar3-version}}) - runs-on: ${{matrix.combo.os}} - strategy: - fail-fast: false - matrix: - combo: - - otp-version: '24.0.2' - rebar3-version: '3.16' - os: 'windows-2019' - - otp-version: '23.0' - rebar3-version: '3.15' - os: 'windows-2019' - - otp-version: '24.0.2' - rebar3-version: '3.16' - os: 'windows-2016' - - otp-version: '23.0' - rebar3-version: '3.15' - os: 'windows-2016' - steps: - - uses: actions/checkout@v2 - - name: Use erlef/setup-beam - id: setup-beam - uses: ./ - with: - otp-version: ${{matrix.combo.otp-version}} - rebar3-version: ${{matrix.combo.rebar3-version}} - - name: Erlang/OTP version (action) - run: echo "Erlang/OTP ${{steps.setup-beam.outputs.otp-version}}" - - name: rebar3 version (action) - run: echo "rebar3 ${{steps.setup-beam.outputs.rebar3-version}}" - - name: Run rebar3 project tests - run: | - cd test-projects/rebar3 - rebar3 ct diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 00000000..6bb1b47b --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,87 @@ +--- +name: windows + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + integration_test: + name: > + OTP ${{matrix.combo.otp-version}}, + Elixir ${{matrix.combo.elixir-version}}, + rebar3 ${{matrix.combo.rebar3-version}} + runs-on: ${{matrix.combo.os}} + strategy: + fail-fast: false + matrix: + combo: + - otp-version: '24.0.2' + rebar3-version: '3.16' + os: 'windows-2019' + - otp-version: '23.0' + rebar3-version: '3.15' + os: 'windows-2019' + - otp-version: '24.0.2' + rebar3-version: '3.16' + os: 'windows-2016' + - otp-version: '23.0' + rebar3-version: '3.15' + os: 'windows-2016' + - otp-version: '22.3' + rebar3-version: '3.15' + os: 'windows-2016' + - otp-version: '22.0' + rebar3-version: '3.15' + os: 'windows-2016' + - otp-version: '21.3' + rebar3-version: '3.15' + os: 'windows-2016' + - otp-version: '21.0' + rebar3-version: '3.15' + os: 'windows-2016' + - elixir-version: 'v1.10' + otp-version: '23' + rebar3-version: '3.14' + os: 'windows-latest' + - elixir-version: 'v1.11' + otp-version: '24' + rebar3-version: '3.15' + os: 'windows-latest' + steps: + - uses: actions/checkout@v2 + - name: Use erlef/setup-beam + id: setup-beam + uses: ./ + with: + otp-version: ${{matrix.combo.otp-version}} + elixir-version: ${{matrix.combo.elixir-version}} + rebar3-version: ${{matrix.combo.rebar3-version}} + - name: Erlang/OTP version (action) + run: echo "Erlang/OTP ${{steps.setup-beam.outputs.otp-version}}" + - name: Elixir version (action) + run: echo "Elixir ${{steps.setup-beam.outputs.elixir-version}}" + if: ${{matrix.combo.elixir-version}} + - name: rebar3 version (action) + run: echo "rebar3 ${{steps.setup-beam.outputs.rebar3-version}}" + - name: mix version and help (CLI) + run: | + mix -v + mix help local.rebar + mix help local.hex + if: ${{matrix.combo.elixir-version}} + - name: Run Mix project tests + run: | + cd test-projects/mix + mix deps.get + mix test + if: ${{matrix.combo.elixir-version}} + - name: Run rebar3 project tests + run: | + cd test-projects/rebar3 + rebar3 ct + if: ${{matrix.combo.rebar3-version}} diff --git a/README.md b/README.md index fcbb8480..65b6ee88 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,12 @@ -# setup-beam [![GitHub Actions Test][test-img]][test] [![GitHub Actions CI][ci-img]][ci] +# setup-beam [![GitHub Actions][action-img]][action] [![GitHub Actions][ubuntu-img]][ubuntu] [![GitHub Actions][windows-img]][windows] -[test]: https://github.com/erlef/setup-beam -[test-img]: https://github.com/erlef/setup-beam/workflows/test/badge.svg -[ci]: https://github.com/erlef/setup-beam -[ci-img]: https://github.com/erlef/setup-beam/workflows/ci/badge.svg +[action]: https://github.com/erlef/setup-beam +[action-img]: https://github.com/erlef/setup-beam/workflows/action/badge.svg +[ubuntu]: https://github.com/erlef/setup-beam +[ubuntu-img]: https://github.com/erlef/setup-beam/workflows/ubuntu/badge.svg +[windows]: https://github.com/erlef/setup-beam +[windows-img]: https://github.com/erlef/setup-beam/workflows/windows/badge.svg This action sets up an Erlang/OTP environment for use in a GitHub Actions workflow by: @@ -14,7 +16,7 @@ workflow by: - optionally, installing `rebar3` - optionally, installing `hex` -**Note** Currently, this action only supports Actions' `ubuntu-` runtimes. +**Note** Currently, this action only supports Actions' `ubuntu-` and `windows-` runtimes. ## Usage @@ -23,9 +25,10 @@ See [action.yml](action.yml) for the action's specification. **Note**: The Erlang/OTP release version specification is [relatively complex](http://erlang.org/doc/system_principles/versions.html#version-scheme). For best results, we recommend specifying exact Erlang/OTP, Elixir versions, and -`rebar3` versions. +`rebar3` versions, and setting option `version-type` to `strict`. However, values like `22.x`, or even `>22`, are also accepted, and we attempt to resolve them -according to semantic versioning rules. +according to semantic versioning rules. This implicitly means `version-type` is `loose`, +which is also the default value for this option. Additionally, it is recommended that one specifies Erlang/OTP, Elixir and `rebar3` versions using YAML strings, as these examples do, so that numbers like `23.0` don't @@ -35,20 +38,22 @@ For pre-release Elixir versions, such as `1.11.0-rc.0`, use the full version specifier (`1.11.0-rc.0`). Pre-release versions are opt-in, so `1.11.x` will not match a pre-release. -### Compatibility between Ubuntu and Erlang/OTP +### Compatibility between Operating System and Erlang/OTP -This list presents the known working version combos between Ubuntu +This list presents the known working version combos between the target operating system and Erlang/OTP. -| Ubuntu | Erlang/OTP | Status -|- |- |- -| ubuntu-16.04 | 17 - 24 | ✅ -| ubuntu-18.04 | 17 - 24 | ✅ -| ubuntu-20.04 | 20 - 24 | ✅ -| windows-2016 | 23 - 24 | ✅ -| windows-2019 | 23 - 24 | ✅ +| Operating system | Erlang/OTP | Status +|- |- |- +| ubuntu-16.04 | 17 - 24 | ✅ +| ubuntu-18.04 | 17 - 24 | ✅ +| ubuntu-20.04 | 20 - 24 | ✅ +| windows-2016 | 21* - 24 | ✅ +| windows-2019 | 21* - 24 | ✅ -### Basic example (Elixir) +**Note** *: prior to 23, Windows builds are only available for minor versions, e.g. 21.0, 21.3, 22.0, etc. + +### Basic example (Erlang/OTP + Elixir, on Ubuntu) ```yaml # create this in .github/workflows/ci.yml @@ -67,7 +72,7 @@ jobs: - run: mix test ``` -### Basic example (`rebar3`) +### Basic example (Erlang/OTP + `rebar3`, on Ubuntu) ```yaml # create this in .github/workflows/ci.yml @@ -85,7 +90,7 @@ jobs: - run: rebar3 ct ``` -### Matrix example (Elixir) +### Matrix example (Erlang/OTP + Elixir, on Ubuntu) ```yaml # create this in .github/workflows/ci.yml @@ -109,7 +114,7 @@ jobs: - run: mix test ``` -### Matrix example (`rebar3`) +### Matrix example (Erlang/OTP + `rebar3`, on Ubuntu) ```yaml # create this in .github/workflows/ci.yml @@ -132,7 +137,7 @@ jobs: - run: rebar3 ct ``` -### Basic example (`rebar3` on Windows 2016) +### Basic example (Erlang/OTP + `rebar3`, on Windows) ```yaml # create this in .github/workflows/ci.yml @@ -145,7 +150,7 @@ jobs: - uses: actions/checkout@v2 - uses: erlef/setup-beam@v1 with: - otp-version: '24.0.2' + otp-version: '24' rebar3-version: '3.16.1' - run: rebar3 ct ``` diff --git a/__tests__/setup-beam.test.js b/__tests__/setup-beam.test.js index 58b99379..1307d945 100644 --- a/__tests__/setup-beam.test.js +++ b/__tests__/setup-beam.test.js @@ -16,6 +16,8 @@ async function all() { await testOTPVersions() await testElixirVersions() await testRebar3Versions() + + await testGetVersionFromSpec() } async function testFailInstallOTP() { @@ -84,37 +86,37 @@ async function testOTPVersions() { if (process.platform === 'linux') { spec = '19.3.x' osVersion = 'ubuntu-16.04' - expected = 'OTP-19.3.6' + expected = 'OTP-19.3.6.13' got = await setupBeam.getOTPVersion(spec, osVersion) assert.deepStrictEqual(got, expected) spec = '^19.3.6' osVersion = 'ubuntu-16.04' - expected = 'OTP-19.3.6' + expected = 'OTP-19.3.6.13' got = await setupBeam.getOTPVersion(spec, osVersion) assert.deepStrictEqual(got, expected) spec = '^19.3' osVersion = 'ubuntu-18.04' - expected = 'OTP-19.3.6' + expected = 'OTP-19.3.6.13' got = await setupBeam.getOTPVersion(spec, osVersion) assert.deepStrictEqual(got, expected) spec = '20' osVersion = 'ubuntu-20.04' - expected = 'OTP-20.3.8' + expected = 'OTP-20.3.8.26' got = await setupBeam.getOTPVersion(spec, osVersion) assert.deepStrictEqual(got, expected) spec = '20.x' osVersion = 'ubuntu-20.04' - expected = 'OTP-20.3.8' + expected = 'OTP-20.3.8.26' got = await setupBeam.getOTPVersion(spec, osVersion) assert.deepStrictEqual(got, expected) spec = '20.0' osVersion = 'ubuntu-20.04' - expected = 'OTP-20.0' + expected = 'OTP-20.0.5' got = await setupBeam.getOTPVersion(spec, osVersion) assert.deepStrictEqual(got, expected) @@ -134,13 +136,13 @@ async function testOTPVersions() { spec = '23.2.x' osVersion = 'windows-2016' - expected = '23.2.7' + expected = '23.2.7.4' got = await setupBeam.getOTPVersion(spec, osVersion) assert.deepStrictEqual(got, expected) spec = '23.0' osVersion = 'windows-2019' - expected = '23.0' + expected = '23.0.4' got = await setupBeam.getOTPVersion(spec, osVersion) assert.deepStrictEqual(got, expected) } @@ -164,11 +166,13 @@ async function testElixirVersions() { got = await setupBeam.getElixirVersion(spec, otpVersion) assert.deepStrictEqual(got, expected) - spec = '1.11.0-rc.0' + simulateInput('version-type', 'strict') + spec = 'v1.11.0-rc.0' otpVersion = 'OTP-23' expected = 'v1.11.0-rc.0-otp-23' got = await setupBeam.getElixirVersion(spec, otpVersion) assert.deepStrictEqual(got, expected) + simulateInput('version-type', 'loose') } async function testRebar3Versions() { @@ -181,8 +185,8 @@ async function testRebar3Versions() { got = await setupBeam.getRebar3Version(spec) assert.deepStrictEqual(got, expected) - spec = '3.10.0' - expected = '3.10.0' + spec = '3.11' + expected = '3.11.1' got = await setupBeam.getRebar3Version(spec) assert.deepStrictEqual(got, expected) @@ -192,6 +196,149 @@ async function testRebar3Versions() { assert.deepStrictEqual(got, expected) } +async function testGetVersionFromSpec() { + let got + let expected + let spec + const versions = [ + '3.2.30.5', + '3.2.3.5', + '1', + '2', + '3.2.3.4.1', + '1.0.9', + '3.2.3.40.1', + '1.0.2', + '2.0', + '2.10', + '2.9', + '1.0', + '3.2.3.4.2', + '1.1.0', + '3.4.5.4', + '3.4.5.3', + '3.4.5.4.1', + '24.0-rc3', + '24.0-rc2', + '24.0', + '23.3.4', + '23.3.3', + '22.3.4.9.1', + '22.3.4.12.1', + '22.3.4.10.1', + 'master', + 'v11.11.0-rc.0-otp-23', + ] + + spec = '1' + expected = '1.1.0' + got = setupBeam.getVersionFromSpec(spec, versions) + assert.deepStrictEqual(got, expected) + + spec = '1.0' + expected = '1.0.9' + got = setupBeam.getVersionFromSpec(spec, versions) + assert.deepStrictEqual(got, expected) + + simulateInput('version-type', 'strict') + spec = '1' + expected = '1' + got = setupBeam.getVersionFromSpec(spec, versions) + assert.deepStrictEqual(got, expected) + simulateInput('version-type', 'loose') + + simulateInput('version-type', 'strict') + spec = '1.0' + expected = '1.0' + got = setupBeam.getVersionFromSpec(spec, versions) + assert.deepStrictEqual(got, expected) + simulateInput('version-type', 'loose') + + spec = '2' + expected = '2.10' + got = setupBeam.getVersionFromSpec(spec, versions) + assert.deepStrictEqual(got, expected) + + spec = '3' + expected = '3.4.5.4.1' + got = setupBeam.getVersionFromSpec(spec, versions) + assert.deepStrictEqual(got, expected) + + spec = '3.2' + expected = '3.2.30.5' + got = setupBeam.getVersionFromSpec(spec, versions) + assert.deepStrictEqual(got, expected) + + spec = '>20' + expected = '24.0' + got = setupBeam.getVersionFromSpec(spec, versions) + assert.deepStrictEqual(got, expected) + + spec = '24.0' + expected = '24.0' + got = setupBeam.getVersionFromSpec(spec, versions) + assert.deepStrictEqual(got, expected) + + simulateInput('version-type', 'strict') + spec = '24.0-rc3' + expected = '24.0-rc3' + got = setupBeam.getVersionFromSpec(spec, versions) + assert.deepStrictEqual(got, expected) + simulateInput('version-type', 'loose') + + spec = '22.3' + expected = '22.3.4.12.1' + got = setupBeam.getVersionFromSpec(spec, versions) + assert.deepStrictEqual(got, expected) + + spec = '23.3.3' + expected = '23.3.3' + got = setupBeam.getVersionFromSpec(spec, versions) + assert.deepStrictEqual(got, expected) + + spec = '24' + expected = '24.0' + got = setupBeam.getVersionFromSpec(spec, versions) + assert.deepStrictEqual(got, expected) + + spec = '23.3' + expected = '23.3.4' + got = setupBeam.getVersionFromSpec(spec, versions) + assert.deepStrictEqual(got, expected) + + simulateInput('version-type', 'strict') + spec = 'master' + expected = 'master' + got = setupBeam.getVersionFromSpec(spec, versions) + assert.deepStrictEqual(got, expected) + simulateInput('version-type', 'loose') + + spec = 'master' + expected = 'master' + got = setupBeam.getVersionFromSpec(spec, versions) + assert.deepStrictEqual(got, expected) + + spec = '11.11' + expected = 'v11.11.0-rc.0-otp-23' + got = setupBeam.getVersionFromSpec(spec, versions) + assert.deepStrictEqual(got, expected) + + spec = '11.11.0' + expected = 'v11.11.0-rc.0-otp-23' + got = setupBeam.getVersionFromSpec(spec, versions) + assert.deepStrictEqual(got, expected) + + spec = 'v11.11' + expected = 'v11.11.0-rc.0-otp-23' + got = setupBeam.getVersionFromSpec(spec, versions) + assert.deepStrictEqual(got, expected) + + spec = 'v11.11.0' + expected = 'v11.11.0-rc.0-otp-23' + got = setupBeam.getVersionFromSpec(spec, versions) + assert.deepStrictEqual(got, expected) +} + function simulateInput(key, value) { process.env[`INPUT_${key.replace(/ /g, '_').toUpperCase()}`] = value } diff --git a/action.yml b/action.yml index 12754821..59063c7a 100644 --- a/action.yml +++ b/action.yml @@ -1,5 +1,5 @@ --- -name: Setup Erlang/OTP with optional Elixir and/or rebar3 +name: Setup Erlang/OTP with optional Elixir (and mix) and/or rebar3 description: > Set up a specific version of Erlang/OTP, Elixir, and/or rebar3 and add the command-line tools to the PATH @@ -13,13 +13,17 @@ inputs: otp-version: description: Version range or exact version of Erlang/OTP to use install-hex: - description: Whether to install Hex + description: Whether to install Hex (with Mix) default: true install-rebar: - description: Whether to install Rebar + description: Whether to install Rebar (with Mix) default: true rebar3-version: description: Version range or exact version of rebar3 to use + version-type: + description: strict means the versions are take as-are; loose means we try + to guess versions based on semver rules + default: loose outputs: elixir-version: description: Exact version of Elixir that was installed diff --git a/dist/index.js b/dist/index.js index d2615a36..85c92e4a 100644 --- a/dist/index.js +++ b/dist/index.js @@ -4599,7 +4599,7 @@ async function installOTP(osVersion, otpVersion) { await exec(__nccwpck_require__.ab + "install-otp.sh", [osVersion, otpVersion]) } else if (OS === 'win32') { const script = __nccwpck_require__.ab + "install-otp.ps1" - await exec(`powershell.exe ${script} -VSN:${otpVersion}`) + await exec(`pwsh.exe ${script} -VSN:${otpVersion}`) } } @@ -4614,7 +4614,7 @@ async function installElixir(elixirVersion) { await exec(__nccwpck_require__.ab + "install-elixir.sh", [elixirVersion]) } else if (OS === 'win32') { const script = __nccwpck_require__.ab + "install-elixir.ps1" - await exec(`powershell.exe ${script} ${elixirVersion}`) + await exec(`pwsh.exe ${script} -VSN:${elixirVersion}`) } } @@ -4629,7 +4629,7 @@ async function installRebar3(rebar3Version) { await exec(__nccwpck_require__.ab + "install-rebar3.sh", [rebar3Version]) } else if (OS === 'win32') { const script = __nccwpck_require__.ab + "install-rebar3.ps1" - await exec(`powershell.exe ${script} -VSN:${rebar3Version}`) + await exec(`pwsh.exe ${script} -VSN:${rebar3Version}`) } } @@ -4659,6 +4659,7 @@ const { exec } = __nccwpck_require__(1514) const path = __nccwpck_require__(5622) const semver = __nccwpck_require__(1383) const https = __nccwpck_require__(7211) +const fs = __nccwpck_require__(5747) const installer = __nccwpck_require__(2127) main().catch((err) => { @@ -4673,19 +4674,15 @@ async function main() { const otpVersion = await installOTP(otpSpec, osVersion) const elixirSpec = core.getInput('elixir-version', { required: false }) - const shouldMixHex = core.getInput('install-hex', { - required: false, - }) - const elixirInstalled = await maybeInstallElixir( - elixirSpec, - otpVersion, - shouldMixHex, - ) + const elixirInstalled = await maybeInstallElixir(elixirSpec, otpVersion) if (elixirInstalled === true) { const shouldMixRebar = core.getInput('install-rebar', { required: false, }) await mix(shouldMixRebar, 'rebar') + const shouldMixHex = core.getInput('install-hex', { + required: false, + }) await mix(shouldMixHex, 'hex') } @@ -4701,16 +4698,22 @@ async function installOTP(otpSpec, osVersion) { await installer.installOTP(osVersion, otpVersion) core.setOutput('otp-version', otpVersion) if (process.platform === 'linux') { - prependToPath(`${process.env.RUNNER_TEMP}/.setup-beam/otp/bin`) + core.addPath(`${process.env.RUNNER_TEMP}/.setup-beam/otp/bin`) } else if (process.platform === 'win32') { - prependToPath(`C:/Program Files/erl-${otpVersion}/bin`) + const otpPath = fs.readFileSync(`${process.env.RUNNER_TEMP}/otp_path.txt`, { + encoding: 'utf8', + flag: 'r', + }) + core.addPath(otpPath) } console.log('##[endgroup]') return otpVersion } -async function maybeInstallElixir(elixirSpec, otpVersion, shouldMixHex) { +async function maybeInstallElixir(elixirSpec, otpVersion) { + let installed = false + if (elixirSpec) { const elixirVersion = await getElixirVersion(elixirSpec, otpVersion) console.log(`##[group]Installing Elixir ${elixirVersion}`) @@ -4720,19 +4723,13 @@ async function maybeInstallElixir(elixirSpec, otpVersion, shouldMixHex) { console.log( `##[add-matcher]${path.join(matchersPath, 'elixir-matchers.json')}`, ) - prependToPath(`${process.env.RUNNER_TEMP}/.setup-beam/elixir/bin`) + core.addPath(`${process.env.RUNNER_TEMP}/.setup-beam/elixir/bin`) console.log('##[endgroup]') - return true - } - - if (shouldMixHex) { - console.log( - "hex will not be installed (overriding default) since Elixir wasn't either", - ) + installed = true } - return false + return installed } async function mix(shouldMix, what) { @@ -4746,18 +4743,20 @@ async function mix(shouldMix, what) { } async function maybeInstallRebar3(rebar3Spec) { + let installed = false + if (rebar3Spec) { const rebar3Version = await getRebar3Version(rebar3Spec) console.log(`##[group]Installing rebar3 ${rebar3Version}`) await installer.installRebar3(rebar3Version) core.setOutput('rebar3-version', rebar3Version) - prependToPath(`${process.env.RUNNER_TEMP}/.setup-beam/rebar3/bin`) + core.addPath(`${process.env.RUNNER_TEMP}/.setup-beam/rebar3/bin`) console.log('##[endgroup]') - return true + installed = true } - return false + return installed } async function getOTPVersion(otpSpec0, osVersion) { @@ -4766,7 +4765,7 @@ async function getOTPVersion(otpSpec0, osVersion) { let otpVersion if (otpSpec[1]) { throw new Error( - `Requested Erlang/OTP version (from spec ${otpSpec0}) ` + + `Requested Erlang/OTP version (${otpSpec0}) ` + "should not contain 'OTP-'", ) } @@ -4778,7 +4777,7 @@ async function getOTPVersion(otpSpec0, osVersion) { } if (otpVersion === null) { throw new Error( - `Requested Erlang/OTP version (from spec ${otpSpec0}) not found in build listing`, + `Requested Erlang/OTP version (${otpSpec0}) not found in version list`, ) } @@ -4793,7 +4792,7 @@ async function getElixirVersion(exSpec0, otpVersion) { let elixirVersion if (exSpec[2]) { throw new Error( - `Requested Elixir / Erlang/OTP version (from spec ${exSpec0} / ${otpVersion}) ` + + `Requested Elixir / Erlang/OTP version (${exSpec0} / ${otpVersion}) ` + "should not contain '-otp-...'", ) } @@ -4802,7 +4801,7 @@ async function getElixirVersion(exSpec0, otpVersion) { } if (!exSpec || elixirVersion === null) { throw new Error( - `Requested Elixir version (from spec ${exSpec0}) not found in build listing`, + `Requested Elixir version (${exSpec0}) not found in version list`, ) } const otpMatch = otpVersion.match(/^(?:OTP-)?([^.]+)/) @@ -4824,8 +4823,8 @@ async function getElixirVersion(exSpec0, otpVersion) { } } else { throw new Error( - `Requested Elixir / Erlang/OTP version (from spec ${exSpec0} / ${otpVersion}) not ` + - 'found in build listing', + `Requested Elixir / Erlang/OTP version (${exSpec0} / ${otpVersion}) not ` + + 'found in version list', ) } @@ -4837,7 +4836,7 @@ async function getRebar3Version(r3Spec) { const rebar3Version = getVersionFromSpec(r3Spec, rebar3Versions) if (rebar3Version === null) { throw new Error( - `Requested rebar3 version (from spec ${r3Spec}) not found in build listing`, + `Requested rebar3 version (${r3Spec}) not found in version list`, ) } @@ -4865,11 +4864,7 @@ async function getOTPVersions(osVersion) { .split('\n') .forEach((line) => { const otpMatch = line.match(/^(OTP-)?([^ ]+)/) - - let otpVersion = otpMatch[2] - if (semver.validRange(otpVersion) && hasPatch(otpVersion)) { - otpVersion = semver.minVersion(otpVersion).version - } + const otpVersion = otpMatch[2] otpVersions.set(otpVersion, otpMatch[0]) // we keep the original for later reference }) } else if (process.platform === 'win32') { @@ -4880,10 +4875,7 @@ async function getOTPVersions(osVersion) { .filter((x) => x.name.match(/^otp_win64_.*.exe$/)) .forEach((x) => { const otpMatch = x.name.match(/^otp_win64_(.*).exe$/) - let otpVersion = otpMatch[1] - if (semver.validRange(otpVersion) && hasPatch(otpVersion)) { - otpVersion = semver.minVersion(otpVersion).version - } + const otpVersion = otpMatch[1] otpVersions.set(otpVersion, otpVersion) }) }) @@ -4934,11 +4926,54 @@ async function getRebar3Versions() { } function getVersionFromSpec(spec, versions) { - if (versions.includes(spec)) { - return spec + let version = null + + if (core.getInput('version-type', { required: false }) === 'strict') { + version = spec + } + + if (version === null) { + // We keep a map of semver => "spec" in order to use semver ranges to find appropriate versions + const versionsMap = versions.sort(sortVersions).reduce((acc, v) => { + try { + acc[semver.coerce(v).version] = v + } catch { + // some stuff can't be coerced, like 'master' + acc[v] = v + } + return acc + }, {}) + const rangeForMax = semver.validRange(spec) + if (rangeForMax) { + version = + versionsMap[semver.maxSatisfying(Object.keys(versionsMap), rangeForMax)] + } else { + version = versionsMap[spec] + } } - return semver.maxSatisfying(versions, semver.validRange(spec)) + return version +} + +function sortVersions(left, right) { + let ret = 0 + const newL = verAsComparableStr(left) + const newR = verAsComparableStr(right) + + function verAsComparableStr(ver) { + const matchGroups = 5 + const verSpec = /([^.]+)?\.?([^.]+)?\.?([^.]+)?\.?([^.]+)?\.?([^.]+)?/ + const matches = ver.match(verSpec).splice(1, matchGroups) + return matches.reduce((acc, v) => acc + (v || '0').padStart(3, '0'), '') + } + + if (newL < newR) { + ret = -1 + } else if (newL > newR) { + ret = 1 + } + + return ret } function getRunnerOSVersion() { @@ -4998,27 +5033,11 @@ async function get(url0, pageIdxs) { return ret } -function prependToPath(what) { - if (process.platform === 'linux') { - process.env.PATH = `${what}:${process.env.PATH}` - } else if (process.platform === 'win32') { - process.env.PATH = `${what};${process.env.PATH}` - } -} - -function hasPatch(v) { - try { - semver.patch(v) - } catch { - return false - } - - return true -} module.exports = { getOTPVersion, getElixirVersion, getRebar3Version, + getVersionFromSpec, } diff --git a/dist/install-elixir.ps1 b/dist/install-elixir.ps1 index 7674016f..f822de2c 100644 --- a/dist/install-elixir.ps1 +++ b/dist/install-elixir.ps1 @@ -1,5 +1,19 @@ -Write-Output "Installer for Elixir for Windows not available" +param([Parameter(Mandatory=$true)][string]$VSN) $ErrorActionPreference="Stop" -Exit 1 +Set-Location $Env:RUNNER_TEMP + +$FILE_INPUT="${VSN}.zip" +$FILE_OUTPUT="elixir.zip" +$DIR_FOR_BIN=".setup-beam/elixir" + +$ProgressPreference="SilentlyContinue" +Invoke-WebRequest "https://repo.hex.pm/builds/elixir/${FILE_INPUT}" -OutFile "$FILE_OUTPUT" +$ProgressPreference="Continue" +New-Item "$DIR_FOR_BIN" -ItemType Directory | Out-Null +$ProgressPreference="SilentlyContinue" +Expand-Archive -DestinationPath "${DIR_FOR_BIN}" -Path "${FILE_OUTPUT}" +$ProgressPreference="Continue" +Write-Output "Installed Erlang/OTP version follows" +& "$DIR_FOR_BIN/bin/iex" "-v" | Write-Output diff --git a/dist/install-elixir.sh b/dist/install-elixir.sh index e7139be5..21f64a29 100755 --- a/dist/install-elixir.sh +++ b/dist/install-elixir.sh @@ -9,12 +9,8 @@ FILE_INPUT="${VSN}.zip" FILE_OUTPUT=elixir.zip DIR_FOR_BIN=.setup-beam/elixir -rm -f "${FILE_OUTPUT}" -rm -rf "${DIR_FOR_BIN}" wget -q -O "${FILE_OUTPUT}" "https://repo.hex.pm/builds/elixir/${FILE_INPUT}" mkdir -p "${DIR_FOR_BIN}" unzip -q -d "${DIR_FOR_BIN}" "${FILE_OUTPUT}" -rm -f "${FILE_OUTPUT}" -echo "$(pwd)/${DIR_FOR_BIN}/bin" >> "$GITHUB_PATH" echo "Installed Elixir version follows" ${DIR_FOR_BIN}/bin/iex -v diff --git a/dist/install-otp.ps1 b/dist/install-otp.ps1 index 7f0c30e1..bfd0adac 100644 --- a/dist/install-otp.ps1 +++ b/dist/install-otp.ps1 @@ -7,13 +7,14 @@ Set-Location $Env:RUNNER_TEMP $FILE_OUTPUT="otp.exe" $DIR_FOR_BIN=".setup-beam/otp" -Remove-Item -Recurse -Force "$DIR_FOR_BIN" -ErrorAction SilentlyContinue $ProgressPreference="SilentlyContinue" Invoke-WebRequest "https://github.com/erlang/otp/releases/download/OTP-$VSN/otp_win64_$VSN.exe" -OutFile "$FILE_OUTPUT" $ProgressPreference="Continue" New-Item "$DIR_FOR_BIN" -ItemType Directory | Out-Null Move-Item "$FILE_OUTPUT" "$DIR_FOR_BIN" Start-Process "./$DIR_FOR_BIN/$FILE_OUTPUT" /S -Wait -Write-Output "C:/Program Files/erl-$VSN/bin" | Out-File -FilePath $Env:GITHUB_PATH -Encoding utf8 -Append +$ErlExec = Get-ChildItem -Path "C:/Program Files/" -Recurse -Depth 2 -Filter 'erl.exe' -Name | ForEach-Object { Write-Output "C:/Program Files/$_" } +$ErlPath = Split-Path -Path "$ErlExec" +Write-Output "$ErlPath" | Out-File -FilePath otp_path.txt -Encoding utf8 -NoNewline Write-Output "Installed Erlang/OTP version follows" -& "C:/Program Files/erl-$VSN/bin/erl.exe" "+V" | Write-Output +& "$ErlPath/erl.exe" "+V" | Write-Output diff --git a/dist/install-otp.sh b/dist/install-otp.sh index b3e740d6..5313e133 100755 --- a/dist/install-otp.sh +++ b/dist/install-otp.sh @@ -10,13 +10,9 @@ FILE_INPUT="${VSN}.tar.gz" FILE_OUTPUT=otp.tar.gz DIR_FOR_BIN=.setup-beam/otp -rm -f "${FILE_OUTPUT}" -rm -rf "${DIR_FOR_BIN}" wget -q -O "${FILE_OUTPUT}" "https://repo.hex.pm/builds/otp/${OS}/${FILE_INPUT}" mkdir -p "${DIR_FOR_BIN}" tar zxf "${FILE_OUTPUT}" -C "${DIR_FOR_BIN}" --strip-components=1 -rm -f "${FILE_OUTPUT}" "${DIR_FOR_BIN}/Install" -minimal "$(pwd)/${DIR_FOR_BIN}" -echo "$(pwd)/${DIR_FOR_BIN}/bin" >> "$GITHUB_PATH" echo "Installed Erlang/OTP version follows" ${DIR_FOR_BIN}/bin/erl -version diff --git a/dist/install-rebar3.ps1 b/dist/install-rebar3.ps1 index 296c93e2..e1aa09f6 100644 --- a/dist/install-rebar3.ps1 +++ b/dist/install-rebar3.ps1 @@ -9,8 +9,6 @@ $FILE_OUTPUT="rebar3" $FILE_OUTPUT_PS1="rebar3.ps1" $DIR_FOR_BIN=".setup-beam/rebar3" -Remove-Item -Force "$FILE_OUTPUT" -ErrorAction SilentlyContinue -Remove-Item -Recurse -Force "$DIR_FOR_BIN" -ErrorAction SilentlyContinue $ProgressPreference="SilentlyContinue" Invoke-WebRequest "https://github.com/erlang/rebar3/releases/download/${VSN}/${FILE_INPUT}" -OutFile "$FILE_OUTPUT" $ProgressPreference="Continue" @@ -19,6 +17,5 @@ Move-Item "$FILE_OUTPUT" "$DIR_FOR_BIN/bin" Write-Output "& escript.exe $PWD/$DIR_FOR_BIN/bin/$FILE_OUTPUT `$args" | Out-File -FilePath "$FILE_OUTPUT_PS1" -Encoding utf8 -Append type $FILE_OUTPUT_PS1 Move-Item "$FILE_OUTPUT_PS1" "$DIR_FOR_BIN/bin" -Write-Output "$PWD/$DIR_FOR_BIN/bin" | Out-File -FilePath $Env:GITHUB_PATH -Encoding utf8 -Append Write-Output "Installed rebar3 version follows" & "$DIR_FOR_BIN/bin/rebar3" "version" | Write-Output diff --git a/dist/install-rebar3.sh b/dist/install-rebar3.sh index 4e918833..2daf23a6 100755 --- a/dist/install-rebar3.sh +++ b/dist/install-rebar3.sh @@ -9,12 +9,9 @@ FILE_INPUT=rebar3 FILE_OUTPUT=rebar3 DIR_FOR_BIN=.setup-beam/rebar3 -rm -f "${FILE_OUTPUT}" -rm -rf "${DIR_FOR_BIN}" wget -q -O "${FILE_OUTPUT}" "https://github.com/erlang/rebar3/releases/download/${VSN}/${FILE_INPUT}" mkdir -p "${DIR_FOR_BIN}/bin" chmod +x "${FILE_OUTPUT}" mv "${FILE_OUTPUT}" "${DIR_FOR_BIN}/bin" -echo "$(pwd)/${DIR_FOR_BIN}/bin" >> "$GITHUB_PATH" echo "Installed rebar3 version follows" ${DIR_FOR_BIN}/bin/rebar3 version diff --git a/src/install-elixir.ps1 b/src/install-elixir.ps1 index 7674016f..f822de2c 100644 --- a/src/install-elixir.ps1 +++ b/src/install-elixir.ps1 @@ -1,5 +1,19 @@ -Write-Output "Installer for Elixir for Windows not available" +param([Parameter(Mandatory=$true)][string]$VSN) $ErrorActionPreference="Stop" -Exit 1 +Set-Location $Env:RUNNER_TEMP + +$FILE_INPUT="${VSN}.zip" +$FILE_OUTPUT="elixir.zip" +$DIR_FOR_BIN=".setup-beam/elixir" + +$ProgressPreference="SilentlyContinue" +Invoke-WebRequest "https://repo.hex.pm/builds/elixir/${FILE_INPUT}" -OutFile "$FILE_OUTPUT" +$ProgressPreference="Continue" +New-Item "$DIR_FOR_BIN" -ItemType Directory | Out-Null +$ProgressPreference="SilentlyContinue" +Expand-Archive -DestinationPath "${DIR_FOR_BIN}" -Path "${FILE_OUTPUT}" +$ProgressPreference="Continue" +Write-Output "Installed Erlang/OTP version follows" +& "$DIR_FOR_BIN/bin/iex" "-v" | Write-Output diff --git a/src/install-elixir.sh b/src/install-elixir.sh index e7139be5..21f64a29 100755 --- a/src/install-elixir.sh +++ b/src/install-elixir.sh @@ -9,12 +9,8 @@ FILE_INPUT="${VSN}.zip" FILE_OUTPUT=elixir.zip DIR_FOR_BIN=.setup-beam/elixir -rm -f "${FILE_OUTPUT}" -rm -rf "${DIR_FOR_BIN}" wget -q -O "${FILE_OUTPUT}" "https://repo.hex.pm/builds/elixir/${FILE_INPUT}" mkdir -p "${DIR_FOR_BIN}" unzip -q -d "${DIR_FOR_BIN}" "${FILE_OUTPUT}" -rm -f "${FILE_OUTPUT}" -echo "$(pwd)/${DIR_FOR_BIN}/bin" >> "$GITHUB_PATH" echo "Installed Elixir version follows" ${DIR_FOR_BIN}/bin/iex -v diff --git a/src/install-otp.ps1 b/src/install-otp.ps1 index 7f0c30e1..bfd0adac 100644 --- a/src/install-otp.ps1 +++ b/src/install-otp.ps1 @@ -7,13 +7,14 @@ Set-Location $Env:RUNNER_TEMP $FILE_OUTPUT="otp.exe" $DIR_FOR_BIN=".setup-beam/otp" -Remove-Item -Recurse -Force "$DIR_FOR_BIN" -ErrorAction SilentlyContinue $ProgressPreference="SilentlyContinue" Invoke-WebRequest "https://github.com/erlang/otp/releases/download/OTP-$VSN/otp_win64_$VSN.exe" -OutFile "$FILE_OUTPUT" $ProgressPreference="Continue" New-Item "$DIR_FOR_BIN" -ItemType Directory | Out-Null Move-Item "$FILE_OUTPUT" "$DIR_FOR_BIN" Start-Process "./$DIR_FOR_BIN/$FILE_OUTPUT" /S -Wait -Write-Output "C:/Program Files/erl-$VSN/bin" | Out-File -FilePath $Env:GITHUB_PATH -Encoding utf8 -Append +$ErlExec = Get-ChildItem -Path "C:/Program Files/" -Recurse -Depth 2 -Filter 'erl.exe' -Name | ForEach-Object { Write-Output "C:/Program Files/$_" } +$ErlPath = Split-Path -Path "$ErlExec" +Write-Output "$ErlPath" | Out-File -FilePath otp_path.txt -Encoding utf8 -NoNewline Write-Output "Installed Erlang/OTP version follows" -& "C:/Program Files/erl-$VSN/bin/erl.exe" "+V" | Write-Output +& "$ErlPath/erl.exe" "+V" | Write-Output diff --git a/src/install-otp.sh b/src/install-otp.sh index b3e740d6..5313e133 100755 --- a/src/install-otp.sh +++ b/src/install-otp.sh @@ -10,13 +10,9 @@ FILE_INPUT="${VSN}.tar.gz" FILE_OUTPUT=otp.tar.gz DIR_FOR_BIN=.setup-beam/otp -rm -f "${FILE_OUTPUT}" -rm -rf "${DIR_FOR_BIN}" wget -q -O "${FILE_OUTPUT}" "https://repo.hex.pm/builds/otp/${OS}/${FILE_INPUT}" mkdir -p "${DIR_FOR_BIN}" tar zxf "${FILE_OUTPUT}" -C "${DIR_FOR_BIN}" --strip-components=1 -rm -f "${FILE_OUTPUT}" "${DIR_FOR_BIN}/Install" -minimal "$(pwd)/${DIR_FOR_BIN}" -echo "$(pwd)/${DIR_FOR_BIN}/bin" >> "$GITHUB_PATH" echo "Installed Erlang/OTP version follows" ${DIR_FOR_BIN}/bin/erl -version diff --git a/src/install-rebar3.ps1 b/src/install-rebar3.ps1 index 296c93e2..e1aa09f6 100644 --- a/src/install-rebar3.ps1 +++ b/src/install-rebar3.ps1 @@ -9,8 +9,6 @@ $FILE_OUTPUT="rebar3" $FILE_OUTPUT_PS1="rebar3.ps1" $DIR_FOR_BIN=".setup-beam/rebar3" -Remove-Item -Force "$FILE_OUTPUT" -ErrorAction SilentlyContinue -Remove-Item -Recurse -Force "$DIR_FOR_BIN" -ErrorAction SilentlyContinue $ProgressPreference="SilentlyContinue" Invoke-WebRequest "https://github.com/erlang/rebar3/releases/download/${VSN}/${FILE_INPUT}" -OutFile "$FILE_OUTPUT" $ProgressPreference="Continue" @@ -19,6 +17,5 @@ Move-Item "$FILE_OUTPUT" "$DIR_FOR_BIN/bin" Write-Output "& escript.exe $PWD/$DIR_FOR_BIN/bin/$FILE_OUTPUT `$args" | Out-File -FilePath "$FILE_OUTPUT_PS1" -Encoding utf8 -Append type $FILE_OUTPUT_PS1 Move-Item "$FILE_OUTPUT_PS1" "$DIR_FOR_BIN/bin" -Write-Output "$PWD/$DIR_FOR_BIN/bin" | Out-File -FilePath $Env:GITHUB_PATH -Encoding utf8 -Append Write-Output "Installed rebar3 version follows" & "$DIR_FOR_BIN/bin/rebar3" "version" | Write-Output diff --git a/src/install-rebar3.sh b/src/install-rebar3.sh index 4e918833..2daf23a6 100755 --- a/src/install-rebar3.sh +++ b/src/install-rebar3.sh @@ -9,12 +9,9 @@ FILE_INPUT=rebar3 FILE_OUTPUT=rebar3 DIR_FOR_BIN=.setup-beam/rebar3 -rm -f "${FILE_OUTPUT}" -rm -rf "${DIR_FOR_BIN}" wget -q -O "${FILE_OUTPUT}" "https://github.com/erlang/rebar3/releases/download/${VSN}/${FILE_INPUT}" mkdir -p "${DIR_FOR_BIN}/bin" chmod +x "${FILE_OUTPUT}" mv "${FILE_OUTPUT}" "${DIR_FOR_BIN}/bin" -echo "$(pwd)/${DIR_FOR_BIN}/bin" >> "$GITHUB_PATH" echo "Installed rebar3 version follows" ${DIR_FOR_BIN}/bin/rebar3 version diff --git a/src/installer.js b/src/installer.js index 275252ff..48609299 100644 --- a/src/installer.js +++ b/src/installer.js @@ -13,7 +13,7 @@ async function installOTP(osVersion, otpVersion) { await exec(path.join(__dirname, 'install-otp.sh'), [osVersion, otpVersion]) } else if (OS === 'win32') { const script = path.join(__dirname, 'install-otp.ps1') - await exec(`powershell.exe ${script} -VSN:${otpVersion}`) + await exec(`pwsh.exe ${script} -VSN:${otpVersion}`) } } @@ -28,7 +28,7 @@ async function installElixir(elixirVersion) { await exec(path.join(__dirname, 'install-elixir.sh'), [elixirVersion]) } else if (OS === 'win32') { const script = path.join(__dirname, 'install-elixir.ps1') - await exec(`powershell.exe ${script} ${elixirVersion}`) + await exec(`pwsh.exe ${script} -VSN:${elixirVersion}`) } } @@ -43,7 +43,7 @@ async function installRebar3(rebar3Version) { await exec(path.join(__dirname, 'install-rebar3.sh'), [rebar3Version]) } else if (OS === 'win32') { const script = path.join(__dirname, 'install-rebar3.ps1') - await exec(`powershell.exe ${script} -VSN:${rebar3Version}`) + await exec(`pwsh.exe ${script} -VSN:${rebar3Version}`) } } diff --git a/src/setup-beam.js b/src/setup-beam.js index 234aa2c6..5210dab0 100644 --- a/src/setup-beam.js +++ b/src/setup-beam.js @@ -3,6 +3,7 @@ const { exec } = require('@actions/exec') const path = require('path') const semver = require('semver') const https = require('https') +const fs = require('fs') const installer = require('./installer') main().catch((err) => { @@ -17,19 +18,15 @@ async function main() { const otpVersion = await installOTP(otpSpec, osVersion) const elixirSpec = core.getInput('elixir-version', { required: false }) - const shouldMixHex = core.getInput('install-hex', { - required: false, - }) - const elixirInstalled = await maybeInstallElixir( - elixirSpec, - otpVersion, - shouldMixHex, - ) + const elixirInstalled = await maybeInstallElixir(elixirSpec, otpVersion) if (elixirInstalled === true) { const shouldMixRebar = core.getInput('install-rebar', { required: false, }) await mix(shouldMixRebar, 'rebar') + const shouldMixHex = core.getInput('install-hex', { + required: false, + }) await mix(shouldMixHex, 'hex') } @@ -45,16 +42,22 @@ async function installOTP(otpSpec, osVersion) { await installer.installOTP(osVersion, otpVersion) core.setOutput('otp-version', otpVersion) if (process.platform === 'linux') { - prependToPath(`${process.env.RUNNER_TEMP}/.setup-beam/otp/bin`) + core.addPath(`${process.env.RUNNER_TEMP}/.setup-beam/otp/bin`) } else if (process.platform === 'win32') { - prependToPath(`C:/Program Files/erl-${otpVersion}/bin`) + const otpPath = fs.readFileSync(`${process.env.RUNNER_TEMP}/otp_path.txt`, { + encoding: 'utf8', + flag: 'r', + }) + core.addPath(otpPath) } console.log('##[endgroup]') return otpVersion } -async function maybeInstallElixir(elixirSpec, otpVersion, shouldMixHex) { +async function maybeInstallElixir(elixirSpec, otpVersion) { + let installed = false + if (elixirSpec) { const elixirVersion = await getElixirVersion(elixirSpec, otpVersion) console.log(`##[group]Installing Elixir ${elixirVersion}`) @@ -64,19 +67,13 @@ async function maybeInstallElixir(elixirSpec, otpVersion, shouldMixHex) { console.log( `##[add-matcher]${path.join(matchersPath, 'elixir-matchers.json')}`, ) - prependToPath(`${process.env.RUNNER_TEMP}/.setup-beam/elixir/bin`) + core.addPath(`${process.env.RUNNER_TEMP}/.setup-beam/elixir/bin`) console.log('##[endgroup]') - return true + installed = true } - if (shouldMixHex) { - console.log( - "hex will not be installed (overriding default) since Elixir wasn't either", - ) - } - - return false + return installed } async function mix(shouldMix, what) { @@ -90,18 +87,20 @@ async function mix(shouldMix, what) { } async function maybeInstallRebar3(rebar3Spec) { + let installed = false + if (rebar3Spec) { const rebar3Version = await getRebar3Version(rebar3Spec) console.log(`##[group]Installing rebar3 ${rebar3Version}`) await installer.installRebar3(rebar3Version) core.setOutput('rebar3-version', rebar3Version) - prependToPath(`${process.env.RUNNER_TEMP}/.setup-beam/rebar3/bin`) + core.addPath(`${process.env.RUNNER_TEMP}/.setup-beam/rebar3/bin`) console.log('##[endgroup]') - return true + installed = true } - return false + return installed } async function getOTPVersion(otpSpec0, osVersion) { @@ -110,7 +109,7 @@ async function getOTPVersion(otpSpec0, osVersion) { let otpVersion if (otpSpec[1]) { throw new Error( - `Requested Erlang/OTP version (from spec ${otpSpec0}) ` + + `Requested Erlang/OTP version (${otpSpec0}) ` + "should not contain 'OTP-'", ) } @@ -122,7 +121,7 @@ async function getOTPVersion(otpSpec0, osVersion) { } if (otpVersion === null) { throw new Error( - `Requested Erlang/OTP version (from spec ${otpSpec0}) not found in build listing`, + `Requested Erlang/OTP version (${otpSpec0}) not found in version list`, ) } @@ -137,7 +136,7 @@ async function getElixirVersion(exSpec0, otpVersion) { let elixirVersion if (exSpec[2]) { throw new Error( - `Requested Elixir / Erlang/OTP version (from spec ${exSpec0} / ${otpVersion}) ` + + `Requested Elixir / Erlang/OTP version (${exSpec0} / ${otpVersion}) ` + "should not contain '-otp-...'", ) } @@ -146,7 +145,7 @@ async function getElixirVersion(exSpec0, otpVersion) { } if (!exSpec || elixirVersion === null) { throw new Error( - `Requested Elixir version (from spec ${exSpec0}) not found in build listing`, + `Requested Elixir version (${exSpec0}) not found in version list`, ) } const otpMatch = otpVersion.match(/^(?:OTP-)?([^.]+)/) @@ -168,8 +167,8 @@ async function getElixirVersion(exSpec0, otpVersion) { } } else { throw new Error( - `Requested Elixir / Erlang/OTP version (from spec ${exSpec0} / ${otpVersion}) not ` + - 'found in build listing', + `Requested Elixir / Erlang/OTP version (${exSpec0} / ${otpVersion}) not ` + + 'found in version list', ) } @@ -181,7 +180,7 @@ async function getRebar3Version(r3Spec) { const rebar3Version = getVersionFromSpec(r3Spec, rebar3Versions) if (rebar3Version === null) { throw new Error( - `Requested rebar3 version (from spec ${r3Spec}) not found in build listing`, + `Requested rebar3 version (${r3Spec}) not found in version list`, ) } @@ -209,11 +208,7 @@ async function getOTPVersions(osVersion) { .split('\n') .forEach((line) => { const otpMatch = line.match(/^(OTP-)?([^ ]+)/) - - let otpVersion = otpMatch[2] - if (semver.validRange(otpVersion) && hasPatch(otpVersion)) { - otpVersion = semver.minVersion(otpVersion).version - } + const otpVersion = otpMatch[2] otpVersions.set(otpVersion, otpMatch[0]) // we keep the original for later reference }) } else if (process.platform === 'win32') { @@ -224,10 +219,7 @@ async function getOTPVersions(osVersion) { .filter((x) => x.name.match(/^otp_win64_.*.exe$/)) .forEach((x) => { const otpMatch = x.name.match(/^otp_win64_(.*).exe$/) - let otpVersion = otpMatch[1] - if (semver.validRange(otpVersion) && hasPatch(otpVersion)) { - otpVersion = semver.minVersion(otpVersion).version - } + const otpVersion = otpMatch[1] otpVersions.set(otpVersion, otpVersion) }) }) @@ -278,11 +270,54 @@ async function getRebar3Versions() { } function getVersionFromSpec(spec, versions) { - if (versions.includes(spec)) { - return spec + let version = null + + if (core.getInput('version-type', { required: false }) === 'strict') { + version = spec + } + + if (version === null) { + // We keep a map of semver => "spec" in order to use semver ranges to find appropriate versions + const versionsMap = versions.sort(sortVersions).reduce((acc, v) => { + try { + acc[semver.coerce(v).version] = v + } catch { + // some stuff can't be coerced, like 'master' + acc[v] = v + } + return acc + }, {}) + const rangeForMax = semver.validRange(spec) + if (rangeForMax) { + version = + versionsMap[semver.maxSatisfying(Object.keys(versionsMap), rangeForMax)] + } else { + version = versionsMap[spec] + } } - return semver.maxSatisfying(versions, semver.validRange(spec)) + return version +} + +function sortVersions(left, right) { + let ret = 0 + const newL = verAsComparableStr(left) + const newR = verAsComparableStr(right) + + function verAsComparableStr(ver) { + const matchGroups = 5 + const verSpec = /([^.]+)?\.?([^.]+)?\.?([^.]+)?\.?([^.]+)?\.?([^.]+)?/ + const matches = ver.match(verSpec).splice(1, matchGroups) + return matches.reduce((acc, v) => acc + (v || '0').padStart(3, '0'), '') + } + + if (newL < newR) { + ret = -1 + } else if (newL > newR) { + ret = 1 + } + + return ret } function getRunnerOSVersion() { @@ -342,25 +377,9 @@ async function get(url0, pageIdxs) { return ret } -function prependToPath(what) { - if (process.platform === 'linux') { - process.env.PATH = `${what}:${process.env.PATH}` - } else if (process.platform === 'win32') { - process.env.PATH = `${what};${process.env.PATH}` - } -} - -function hasPatch(v) { - try { - semver.patch(v) - } catch { - return false - } - - return true -} module.exports = { getOTPVersion, getElixirVersion, getRebar3Version, + getVersionFromSpec, }