neiam
/action-setup-beam
action-setup-beam
public · Issues · Pulls · Labels · Forks · Compare · Actions queued
⭐
Log in to mark this repository.
Improve visibility (+ fix bugs) over version-type and opt-in versions (#57)
8306425 · Paulo F. Oliveira · 2021-07-09 09:11
Message
{commit_body(@commit)}
Files changed
modified
.github/workflows/ubuntu.yml
+12
−0
@@ -92,6 +92,17 @@ jobs:
| 92 | 92 | otp-version: '24' |
| 93 | 93 | rebar3-version: '3.15' |
| 94 | 94 | os: 'ubuntu-20.04' |
| 95 | + - elixir-version: 'v1.11.0' | |
| 96 | + otp-version: '22.3.4.2' | |
| 97 | + os: 'ubuntu-20.04' | |
| 98 | + version-type: 'strict' | |
| 99 | + - elixir-version: '1.10.3' | |
| 100 | + otp-version: '22.3.4.1' | |
| 101 | + os: 'ubuntu-20.04' | |
| 102 | + version-type: 'strict' | |
| 103 | + - elixir-version: '1.10.3' | |
| 104 | + otp-version: '22.3.4.1' | |
| 105 | + os: 'ubuntu-20.04' | |
| 95 | 106 | steps: |
| 96 | 107 | - uses: actions/checkout@v2 |
| 97 | 108 | - name: Use erlef/setup-beam |
@@ -101,6 +112,7 @@ jobs:
| 101 | 112 | otp-version: ${{matrix.combo.otp-version}} |
| 102 | 113 | elixir-version: ${{matrix.combo.elixir-version}} |
| 103 | 114 | rebar3-version: ${{matrix.combo.rebar3-version}} |
| 115 | + version-type: ${{matrix.combo.version-type}} | |
| 104 | 116 | - name: Erlang/OTP version (action) |
| 105 | 117 | run: echo "Erlang/OTP ${{steps.setup-beam.outputs.otp-version}}" |
| 106 | 118 | - name: Elixir version (action) |
modified
README.md
+15
−3
@@ -34,9 +34,9 @@ Additionally, it is recommended that one specifies Erlang/OTP, Elixir and `rebar
| 34 | 34 | using YAML strings, as these examples do, so that numbers like `23.0` don't |
| 35 | 35 | end up being parsed as `23`, which is not equivalent. |
| 36 | 36 | |
| 37 | −For pre-release Elixir versions, such as `1.11.0-rc.0`, use the full version | |
| 38 | −specifier (`1.11.0-rc.0`). Pre-release versions are opt-in, so `1.11.x` will | |
| 39 | −not match a pre-release. | |
| 37 | +For pre-release versions, such as `v1.11.0-rc.0`, use the full version | |
| 38 | +specifier (`v1.11.0-rc.0`) and set option `version-type` to `strict`. Pre-release versions are | |
| 39 | +opt-in, so `1.11.x` will not match a pre-release. | |
| 40 | 40 | |
| 41 | 41 | ### Compatibility between Operating System and Erlang/OTP |
| 42 | 42 |
@@ -159,6 +159,18 @@ jobs:
| 159 | 159 | |
| 160 | 160 | The Elixir Problem Matchers in this repository are adapted from [here](https://github.com/fr1zle/vscode-elixir/blob/45eddb589acd7ac98e0c7305d1c2b24668ca709a/package.json#L70-L118). See [MATCHER_NOTICE](MATCHER_NOTICE.md) for license details. |
| 161 | 161 | |
| 162 | +## Action versioning | |
| 163 | + | |
| 164 | +`setup-beam` has three version paths, described below, for example version `1.8.0`: | |
| 165 | + | |
| 166 | +- `@v1`: the latest in the `1.y.z` series (this tag is movable), | |
| 167 | +- `@v1.8`: the latest in the `1.8.z` series (this tag is movable), | |
| 168 | +- `@v1.8.0`: release `1.8.0` (this tag is not movable). | |
| 169 | + | |
| 170 | +To prevent issues in CI (unless you're OK with potential incompatibility between versions - we | |
| 171 | +make real a effort to not introduce those without bumping major) we suggest you to use `@vx.y.z`, | |
| 172 | +whenever possible. | |
| 173 | + | |
| 162 | 174 | ## License |
| 163 | 175 | |
| 164 | 176 | The scripts and documentation in this project are released under the [MIT license](LICENSE.md). |
modified
__tests__/setup-beam.test.js
+24
−17
@@ -166,6 +166,12 @@ async function testElixirVersions() {
| 166 | 166 | got = await setupBeam.getElixirVersion(spec, otpVersion) |
| 167 | 167 | assert.deepStrictEqual(got, expected) |
| 168 | 168 | |
| 169 | + spec = '1.12.1' | |
| 170 | + otpVersion = 'OTP-24.0.2' | |
| 171 | + expected = 'v1.12.1-otp-24' | |
| 172 | + got = await setupBeam.getElixirVersion(spec, otpVersion) | |
| 173 | + assert.deepStrictEqual(got, expected) | |
| 174 | + | |
| 169 | 175 | simulateInput('version-type', 'strict') |
| 170 | 176 | spec = 'v1.11.0-rc.0' |
| 171 | 177 | otpVersion = 'OTP-23' |
@@ -173,6 +179,14 @@ async function testElixirVersions() {
| 173 | 179 | got = await setupBeam.getElixirVersion(spec, otpVersion) |
| 174 | 180 | assert.deepStrictEqual(got, expected) |
| 175 | 181 | simulateInput('version-type', 'loose') |
| 182 | + | |
| 183 | + simulateInput('version-type', 'strict') | |
| 184 | + spec = 'v1.11.0' | |
| 185 | + otpVersion = '22.3.4.2' | |
| 186 | + expected = 'v1.11.0-otp-22' | |
| 187 | + got = await setupBeam.getElixirVersion(spec, otpVersion) | |
| 188 | + assert.deepStrictEqual(got, expected) | |
| 189 | + simulateInput('version-type', 'loose') | |
| 176 | 190 | } |
| 177 | 191 | |
| 178 | 192 | async function testRebar3Versions() { |
@@ -228,6 +242,7 @@ async function testGetVersionFromSpec() {
| 228 | 242 | '22.3.4.10.1', |
| 229 | 243 | 'master', |
| 230 | 244 | 'v11.11.0-rc.0-otp-23', |
| 245 | + '22.3.4.2', | |
| 231 | 246 | ] |
| 232 | 247 | |
| 233 | 248 | spec = '1' |
@@ -286,6 +301,11 @@ async function testGetVersionFromSpec() {
| 286 | 301 | assert.deepStrictEqual(got, expected) |
| 287 | 302 | simulateInput('version-type', 'loose') |
| 288 | 303 | |
| 304 | + spec = '24.0-rc2' | |
| 305 | + expected = '24.0-rc2' | |
| 306 | + got = setupBeam.getVersionFromSpec(spec, versions) | |
| 307 | + assert.deepStrictEqual(got, expected) | |
| 308 | + | |
| 289 | 309 | spec = '22.3' |
| 290 | 310 | expected = '22.3.4.12.1' |
| 291 | 311 | got = setupBeam.getVersionFromSpec(spec, versions) |
@@ -318,25 +338,12 @@ async function testGetVersionFromSpec() {
| 318 | 338 | got = setupBeam.getVersionFromSpec(spec, versions) |
| 319 | 339 | assert.deepStrictEqual(got, expected) |
| 320 | 340 | |
| 321 | − spec = '11.11' | |
| 322 | − expected = 'v11.11.0-rc.0-otp-23' | |
| 323 | − got = setupBeam.getVersionFromSpec(spec, versions) | |
| 324 | − assert.deepStrictEqual(got, expected) | |
| 325 | − | |
| 326 | − spec = '11.11.0' | |
| 327 | − expected = 'v11.11.0-rc.0-otp-23' | |
| 328 | − got = setupBeam.getVersionFromSpec(spec, versions) | |
| 329 | − assert.deepStrictEqual(got, expected) | |
| 330 | − | |
| 331 | − spec = 'v11.11' | |
| 332 | − expected = 'v11.11.0-rc.0-otp-23' | |
| 333 | − got = setupBeam.getVersionFromSpec(spec, versions) | |
| 334 | − assert.deepStrictEqual(got, expected) | |
| 335 | − | |
| 336 | − spec = 'v11.11.0' | |
| 337 | − expected = 'v11.11.0-rc.0-otp-23' | |
| 341 | + simulateInput('version-type', 'strict') | |
| 342 | + spec = '22.3.4.2' | |
| 343 | + expected = '22.3.4.2' | |
| 338 | 344 | got = setupBeam.getVersionFromSpec(spec, versions) |
| 339 | 345 | assert.deepStrictEqual(got, expected) |
| 346 | + simulateInput('version-type', 'loose') | |
| 340 | 347 | } |
| 341 | 348 | |
| 342 | 349 | function simulateInput(key, value) { |
modified
dist/index.js
+31
−14
@@ -4777,7 +4777,8 @@ async function getOTPVersion(otpSpec0, osVersion) {
| 4777 | 4777 | } |
| 4778 | 4778 | if (otpVersion === null) { |
| 4779 | 4779 | throw new Error( |
| 4780 | − `Requested Erlang/OTP version (${otpSpec0}) not found in version list`, | |
| 4780 | + `Requested Erlang/OTP version (${otpSpec0}) not found in version list ` + | |
| 4781 | + "(should you be using option 'version-type': 'strict'?)", | |
| 4781 | 4782 | ) |
| 4782 | 4783 | } |
| 4783 | 4784 |
@@ -4788,7 +4789,7 @@ async function getElixirVersion(exSpec0, otpVersion) {
| 4788 | 4789 | const elixirVersions = await getElixirVersions() |
| 4789 | 4790 | const semverVersions = Array.from(elixirVersions.keys()).sort() |
| 4790 | 4791 | |
| 4791 | − const exSpec = exSpec0.match(/^(.+)(-otp-.+)/) || exSpec0.match(/^(.+)/) | |
| 4792 | + const exSpec = exSpec0.match(/^v?(.+)(-otp-.+)/) || exSpec0.match(/^v?(.+)/) | |
| 4792 | 4793 | let elixirVersion |
| 4793 | 4794 | if (exSpec[2]) { |
| 4794 | 4795 | throw new Error( |
@@ -4801,7 +4802,8 @@ async function getElixirVersion(exSpec0, otpVersion) {
| 4801 | 4802 | } |
| 4802 | 4803 | if (!exSpec || elixirVersion === null) { |
| 4803 | 4804 | throw new Error( |
| 4804 | − `Requested Elixir version (${exSpec0}) not found in version list`, | |
| 4805 | + `Requested Elixir version (${exSpec0}) not found in version list ` + | |
| 4806 | + "(should you be using option 'version-type': 'strict'?)", | |
| 4805 | 4807 | ) |
| 4806 | 4808 | } |
| 4807 | 4809 | const otpMatch = otpVersion.match(/^(?:OTP-)?([^.]+)/) |
@@ -4824,11 +4826,11 @@ async function getElixirVersion(exSpec0, otpVersion) {
| 4824 | 4826 | } else { |
| 4825 | 4827 | throw new Error( |
| 4826 | 4828 | `Requested Elixir / Erlang/OTP version (${exSpec0} / ${otpVersion}) not ` + |
| 4827 | − 'found in version list', | |
| 4829 | + "found in version list (should you be using option 'version-type': 'strict'?)", | |
| 4828 | 4830 | ) |
| 4829 | 4831 | } |
| 4830 | 4832 | |
| 4831 | − return elixirVersionWithOTP | |
| 4833 | + return `v${elixirVersionWithOTP}` | |
| 4832 | 4834 | } |
| 4833 | 4835 | |
| 4834 | 4836 | async function getRebar3Version(r3Spec) { |
@@ -4836,7 +4838,8 @@ async function getRebar3Version(r3Spec) {
| 4836 | 4838 | const rebar3Version = getVersionFromSpec(r3Spec, rebar3Versions) |
| 4837 | 4839 | if (rebar3Version === null) { |
| 4838 | 4840 | throw new Error( |
| 4839 | − `Requested rebar3 version (${r3Spec}) not found in version list`, | |
| 4841 | + `Requested rebar3 version (${r3Spec}) not found in version list ` + | |
| 4842 | + "(should you be using option 'version-type': 'strict'?)", | |
| 4840 | 4843 | ) |
| 4841 | 4844 | } |
| 4842 | 4845 |
@@ -4896,7 +4899,7 @@ async function getElixirVersions() {
| 4896 | 4899 | .split('\n') |
| 4897 | 4900 | .forEach((line) => { |
| 4898 | 4901 | const elixirMatch = |
| 4899 | − line.match(/^(.+)-otp-([^ ]+)/) || line.match(/^([^ ]+)/) | |
| 4902 | + line.match(/^v?(.+)-otp-([^ ]+)/) || line.match(/^v?([^ ]+)/) | |
| 4900 | 4903 | const elixirVersion = elixirMatch[1] |
| 4901 | 4904 | const otpVersion = elixirMatch[2] |
| 4902 | 4905 | const otpVersions = otpVersionsForElixirMap.get(elixirVersion) || [] |
@@ -4928,18 +4931,19 @@ async function getRebar3Versions() {
| 4928 | 4931 | function getVersionFromSpec(spec, versions) { |
| 4929 | 4932 | let version = null |
| 4930 | 4933 | |
| 4931 | − if (core.getInput('version-type', { required: false }) === 'strict') { | |
| 4934 | + if ( | |
| 4935 | + spec.match(/rc/) || | |
| 4936 | + core.getInput('version-type', { required: false }) === 'strict' | |
| 4937 | + ) { | |
| 4932 | 4938 | version = spec |
| 4933 | 4939 | } |
| 4934 | 4940 | |
| 4935 | 4941 | if (version === null) { |
| 4936 | 4942 | // We keep a map of semver => "spec" in order to use semver ranges to find appropriate versions |
| 4937 | 4943 | const versionsMap = versions.sort(sortVersions).reduce((acc, v) => { |
| 4938 | − try { | |
| 4939 | − acc[semver.coerce(v).version] = v | |
| 4940 | − } catch { | |
| 4941 | − // some stuff can't be coerced, like 'master' | |
| 4942 | − acc[v] = v | |
| 4944 | + if (!v.match(/rc/)) { | |
| 4945 | + // release candidates are opt-in | |
| 4946 | + acc[maybeCoerced(v)] = v | |
| 4943 | 4947 | } |
| 4944 | 4948 | return acc |
| 4945 | 4949 | }, {}) |
@@ -4948,13 +4952,26 @@ function getVersionFromSpec(spec, versions) {
| 4948 | 4952 | version = |
| 4949 | 4953 | versionsMap[semver.maxSatisfying(Object.keys(versionsMap), rangeForMax)] |
| 4950 | 4954 | } else { |
| 4951 | − version = versionsMap[spec] | |
| 4955 | + version = versionsMap[maybeCoerced(spec)] | |
| 4952 | 4956 | } |
| 4953 | 4957 | } |
| 4954 | 4958 | |
| 4955 | 4959 | return version |
| 4956 | 4960 | } |
| 4957 | 4961 | |
| 4962 | +function maybeCoerced(v) { | |
| 4963 | + let ret | |
| 4964 | + | |
| 4965 | + try { | |
| 4966 | + ret = semver.coerce(v).version | |
| 4967 | + } catch { | |
| 4968 | + // some stuff can't be coerced, like 'master' | |
| 4969 | + ret = v | |
| 4970 | + } | |
| 4971 | + | |
| 4972 | + return ret | |
| 4973 | +} | |
| 4974 | + | |
| 4958 | 4975 | function sortVersions(left, right) { |
| 4959 | 4976 | let ret = 0 |
| 4960 | 4977 | const newL = verAsComparableStr(left) |
modified
src/setup-beam.js
+31
−14
@@ -121,7 +121,8 @@ async function getOTPVersion(otpSpec0, osVersion) {
| 121 | 121 | } |
| 122 | 122 | if (otpVersion === null) { |
| 123 | 123 | throw new Error( |
| 124 | − `Requested Erlang/OTP version (${otpSpec0}) not found in version list`, | |
| 124 | + `Requested Erlang/OTP version (${otpSpec0}) not found in version list ` + | |
| 125 | + "(should you be using option 'version-type': 'strict'?)", | |
| 125 | 126 | ) |
| 126 | 127 | } |
| 127 | 128 |
@@ -132,7 +133,7 @@ async function getElixirVersion(exSpec0, otpVersion) {
| 132 | 133 | const elixirVersions = await getElixirVersions() |
| 133 | 134 | const semverVersions = Array.from(elixirVersions.keys()).sort() |
| 134 | 135 | |
| 135 | − const exSpec = exSpec0.match(/^(.+)(-otp-.+)/) || exSpec0.match(/^(.+)/) | |
| 136 | + const exSpec = exSpec0.match(/^v?(.+)(-otp-.+)/) || exSpec0.match(/^v?(.+)/) | |
| 136 | 137 | let elixirVersion |
| 137 | 138 | if (exSpec[2]) { |
| 138 | 139 | throw new Error( |
@@ -145,7 +146,8 @@ async function getElixirVersion(exSpec0, otpVersion) {
| 145 | 146 | } |
| 146 | 147 | if (!exSpec || elixirVersion === null) { |
| 147 | 148 | throw new Error( |
| 148 | − `Requested Elixir version (${exSpec0}) not found in version list`, | |
| 149 | + `Requested Elixir version (${exSpec0}) not found in version list ` + | |
| 150 | + "(should you be using option 'version-type': 'strict'?)", | |
| 149 | 151 | ) |
| 150 | 152 | } |
| 151 | 153 | const otpMatch = otpVersion.match(/^(?:OTP-)?([^.]+)/) |
@@ -168,11 +170,11 @@ async function getElixirVersion(exSpec0, otpVersion) {
| 168 | 170 | } else { |
| 169 | 171 | throw new Error( |
| 170 | 172 | `Requested Elixir / Erlang/OTP version (${exSpec0} / ${otpVersion}) not ` + |
| 171 | − 'found in version list', | |
| 173 | + "found in version list (should you be using option 'version-type': 'strict'?)", | |
| 172 | 174 | ) |
| 173 | 175 | } |
| 174 | 176 | |
| 175 | − return elixirVersionWithOTP | |
| 177 | + return `v${elixirVersionWithOTP}` | |
| 176 | 178 | } |
| 177 | 179 | |
| 178 | 180 | async function getRebar3Version(r3Spec) { |
@@ -180,7 +182,8 @@ async function getRebar3Version(r3Spec) {
| 180 | 182 | const rebar3Version = getVersionFromSpec(r3Spec, rebar3Versions) |
| 181 | 183 | if (rebar3Version === null) { |
| 182 | 184 | throw new Error( |
| 183 | − `Requested rebar3 version (${r3Spec}) not found in version list`, | |
| 185 | + `Requested rebar3 version (${r3Spec}) not found in version list ` + | |
| 186 | + "(should you be using option 'version-type': 'strict'?)", | |
| 184 | 187 | ) |
| 185 | 188 | } |
| 186 | 189 |
@@ -240,7 +243,7 @@ async function getElixirVersions() {
| 240 | 243 | .split('\n') |
| 241 | 244 | .forEach((line) => { |
| 242 | 245 | const elixirMatch = |
| 243 | − line.match(/^(.+)-otp-([^ ]+)/) || line.match(/^([^ ]+)/) | |
| 246 | + line.match(/^v?(.+)-otp-([^ ]+)/) || line.match(/^v?([^ ]+)/) | |
| 244 | 247 | const elixirVersion = elixirMatch[1] |
| 245 | 248 | const otpVersion = elixirMatch[2] |
| 246 | 249 | const otpVersions = otpVersionsForElixirMap.get(elixirVersion) || [] |
@@ -272,18 +275,19 @@ async function getRebar3Versions() {
| 272 | 275 | function getVersionFromSpec(spec, versions) { |
| 273 | 276 | let version = null |
| 274 | 277 | |
| 275 | − if (core.getInput('version-type', { required: false }) === 'strict') { | |
| 278 | + if ( | |
| 279 | + spec.match(/rc/) || | |
| 280 | + core.getInput('version-type', { required: false }) === 'strict' | |
| 281 | + ) { | |
| 276 | 282 | version = spec |
| 277 | 283 | } |
| 278 | 284 | |
| 279 | 285 | if (version === null) { |
| 280 | 286 | // We keep a map of semver => "spec" in order to use semver ranges to find appropriate versions |
| 281 | 287 | const versionsMap = versions.sort(sortVersions).reduce((acc, v) => { |
| 282 | − try { | |
| 283 | − acc[semver.coerce(v).version] = v | |
| 284 | − } catch { | |
| 285 | − // some stuff can't be coerced, like 'master' | |
| 286 | − acc[v] = v | |
| 288 | + if (!v.match(/rc/)) { | |
| 289 | + // release candidates are opt-in | |
| 290 | + acc[maybeCoerced(v)] = v | |
| 287 | 291 | } |
| 288 | 292 | return acc |
| 289 | 293 | }, {}) |
@@ -292,13 +296,26 @@ function getVersionFromSpec(spec, versions) {
| 292 | 296 | version = |
| 293 | 297 | versionsMap[semver.maxSatisfying(Object.keys(versionsMap), rangeForMax)] |
| 294 | 298 | } else { |
| 295 | − version = versionsMap[spec] | |
| 299 | + version = versionsMap[maybeCoerced(spec)] | |
| 296 | 300 | } |
| 297 | 301 | } |
| 298 | 302 | |
| 299 | 303 | return version |
| 300 | 304 | } |
| 301 | 305 | |
| 306 | +function maybeCoerced(v) { | |
| 307 | + let ret | |
| 308 | + | |
| 309 | + try { | |
| 310 | + ret = semver.coerce(v).version | |
| 311 | + } catch { | |
| 312 | + // some stuff can't be coerced, like 'master' | |
| 313 | + ret = v | |
| 314 | + } | |
| 315 | + | |
| 316 | + return ret | |
| 317 | +} | |
| 318 | + | |
| 302 | 319 | function sortVersions(left, right) { |
| 303 | 320 | let ret = 0 |
| 304 | 321 | const newL = verAsComparableStr(left) |
Parents: d8a37f5