Improve visibility (+ fix bugs) over version-type and opt-in versions (#57)

(and tweak README regarding action versioning)
This commit is contained in:
Paulo F. Oliveira
2021-07-09 10:11:50 +01:00
committed by GitHub
parent d8a37f5aac
commit 830642517c
5 changed files with 113 additions and 48 deletions
+12
View File
@@ -92,6 +92,17 @@ jobs:
otp-version: '24' otp-version: '24'
rebar3-version: '3.15' rebar3-version: '3.15'
os: 'ubuntu-20.04' os: 'ubuntu-20.04'
- elixir-version: 'v1.11.0'
otp-version: '22.3.4.2'
os: 'ubuntu-20.04'
version-type: 'strict'
- elixir-version: '1.10.3'
otp-version: '22.3.4.1'
os: 'ubuntu-20.04'
version-type: 'strict'
- elixir-version: '1.10.3'
otp-version: '22.3.4.1'
os: 'ubuntu-20.04'
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Use erlef/setup-beam - name: Use erlef/setup-beam
@@ -101,6 +112,7 @@ jobs:
otp-version: ${{matrix.combo.otp-version}} otp-version: ${{matrix.combo.otp-version}}
elixir-version: ${{matrix.combo.elixir-version}} elixir-version: ${{matrix.combo.elixir-version}}
rebar3-version: ${{matrix.combo.rebar3-version}} rebar3-version: ${{matrix.combo.rebar3-version}}
version-type: ${{matrix.combo.version-type}}
- name: Erlang/OTP version (action) - name: Erlang/OTP version (action)
run: echo "Erlang/OTP ${{steps.setup-beam.outputs.otp-version}}" run: echo "Erlang/OTP ${{steps.setup-beam.outputs.otp-version}}"
- name: Elixir version (action) - name: Elixir version (action)
+15 -3
View File
@@ -34,9 +34,9 @@ Additionally, it is recommended that one specifies Erlang/OTP, Elixir and `rebar
using YAML strings, as these examples do, so that numbers like `23.0` don't using YAML strings, as these examples do, so that numbers like `23.0` don't
end up being parsed as `23`, which is not equivalent. end up being parsed as `23`, which is not equivalent.
For pre-release Elixir versions, such as `1.11.0-rc.0`, use the full version For pre-release versions, such as `v1.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 specifier (`v1.11.0-rc.0`) and set option `version-type` to `strict`. Pre-release versions are
not match a pre-release. opt-in, so `1.11.x` will not match a pre-release.
### Compatibility between Operating System and Erlang/OTP ### Compatibility between Operating System and Erlang/OTP
@@ -159,6 +159,18 @@ jobs:
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. 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.
## Action versioning
`setup-beam` has three version paths, described below, for example version `1.8.0`:
- `@v1`: the latest in the `1.y.z` series (this tag is movable),
- `@v1.8`: the latest in the `1.8.z` series (this tag is movable),
- `@v1.8.0`: release `1.8.0` (this tag is not movable).
To prevent issues in CI (unless you're OK with potential incompatibility between versions - we
make real a effort to not introduce those without bumping major) we suggest you to use `@vx.y.z`,
whenever possible.
## License ## License
The scripts and documentation in this project are released under the [MIT license](LICENSE.md). The scripts and documentation in this project are released under the [MIT license](LICENSE.md).
+24 -17
View File
@@ -166,6 +166,12 @@ async function testElixirVersions() {
got = await setupBeam.getElixirVersion(spec, otpVersion) got = await setupBeam.getElixirVersion(spec, otpVersion)
assert.deepStrictEqual(got, expected) assert.deepStrictEqual(got, expected)
spec = '1.12.1'
otpVersion = 'OTP-24.0.2'
expected = 'v1.12.1-otp-24'
got = await setupBeam.getElixirVersion(spec, otpVersion)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', 'strict') simulateInput('version-type', 'strict')
spec = 'v1.11.0-rc.0' spec = 'v1.11.0-rc.0'
otpVersion = 'OTP-23' otpVersion = 'OTP-23'
@@ -173,6 +179,14 @@ async function testElixirVersions() {
got = await setupBeam.getElixirVersion(spec, otpVersion) got = await setupBeam.getElixirVersion(spec, otpVersion)
assert.deepStrictEqual(got, expected) assert.deepStrictEqual(got, expected)
simulateInput('version-type', 'loose') simulateInput('version-type', 'loose')
simulateInput('version-type', 'strict')
spec = 'v1.11.0'
otpVersion = '22.3.4.2'
expected = 'v1.11.0-otp-22'
got = await setupBeam.getElixirVersion(spec, otpVersion)
assert.deepStrictEqual(got, expected)
simulateInput('version-type', 'loose')
} }
async function testRebar3Versions() { async function testRebar3Versions() {
@@ -228,6 +242,7 @@ async function testGetVersionFromSpec() {
'22.3.4.10.1', '22.3.4.10.1',
'master', 'master',
'v11.11.0-rc.0-otp-23', 'v11.11.0-rc.0-otp-23',
'22.3.4.2',
] ]
spec = '1' spec = '1'
@@ -286,6 +301,11 @@ async function testGetVersionFromSpec() {
assert.deepStrictEqual(got, expected) assert.deepStrictEqual(got, expected)
simulateInput('version-type', 'loose') simulateInput('version-type', 'loose')
spec = '24.0-rc2'
expected = '24.0-rc2'
got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected)
spec = '22.3' spec = '22.3'
expected = '22.3.4.12.1' expected = '22.3.4.12.1'
got = setupBeam.getVersionFromSpec(spec, versions) got = setupBeam.getVersionFromSpec(spec, versions)
@@ -318,25 +338,12 @@ async function testGetVersionFromSpec() {
got = setupBeam.getVersionFromSpec(spec, versions) got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected) assert.deepStrictEqual(got, expected)
spec = '11.11' simulateInput('version-type', 'strict')
expected = 'v11.11.0-rc.0-otp-23' spec = '22.3.4.2'
got = setupBeam.getVersionFromSpec(spec, versions) expected = '22.3.4.2'
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) got = setupBeam.getVersionFromSpec(spec, versions)
assert.deepStrictEqual(got, expected) assert.deepStrictEqual(got, expected)
simulateInput('version-type', 'loose')
} }
function simulateInput(key, value) { function simulateInput(key, value) {
+31 -14
View File
@@ -4777,7 +4777,8 @@ async function getOTPVersion(otpSpec0, osVersion) {
} }
if (otpVersion === null) { if (otpVersion === null) {
throw new Error( throw new Error(
`Requested Erlang/OTP version (${otpSpec0}) not found in version list`, `Requested Erlang/OTP version (${otpSpec0}) not found in version list ` +
"(should you be using option 'version-type': 'strict'?)",
) )
} }
@@ -4788,7 +4789,7 @@ async function getElixirVersion(exSpec0, otpVersion) {
const elixirVersions = await getElixirVersions() const elixirVersions = await getElixirVersions()
const semverVersions = Array.from(elixirVersions.keys()).sort() const semverVersions = Array.from(elixirVersions.keys()).sort()
const exSpec = exSpec0.match(/^(.+)(-otp-.+)/) || exSpec0.match(/^(.+)/) const exSpec = exSpec0.match(/^v?(.+)(-otp-.+)/) || exSpec0.match(/^v?(.+)/)
let elixirVersion let elixirVersion
if (exSpec[2]) { if (exSpec[2]) {
throw new Error( throw new Error(
@@ -4801,7 +4802,8 @@ async function getElixirVersion(exSpec0, otpVersion) {
} }
if (!exSpec || elixirVersion === null) { if (!exSpec || elixirVersion === null) {
throw new Error( throw new Error(
`Requested Elixir version (${exSpec0}) not found in version list`, `Requested Elixir version (${exSpec0}) not found in version list ` +
"(should you be using option 'version-type': 'strict'?)",
) )
} }
const otpMatch = otpVersion.match(/^(?:OTP-)?([^.]+)/) const otpMatch = otpVersion.match(/^(?:OTP-)?([^.]+)/)
@@ -4824,11 +4826,11 @@ async function getElixirVersion(exSpec0, otpVersion) {
} else { } else {
throw new Error( throw new Error(
`Requested Elixir / Erlang/OTP version (${exSpec0} / ${otpVersion}) not ` + `Requested Elixir / Erlang/OTP version (${exSpec0} / ${otpVersion}) not ` +
'found in version list', "found in version list (should you be using option 'version-type': 'strict'?)",
) )
} }
return elixirVersionWithOTP return `v${elixirVersionWithOTP}`
} }
async function getRebar3Version(r3Spec) { async function getRebar3Version(r3Spec) {
@@ -4836,7 +4838,8 @@ async function getRebar3Version(r3Spec) {
const rebar3Version = getVersionFromSpec(r3Spec, rebar3Versions) const rebar3Version = getVersionFromSpec(r3Spec, rebar3Versions)
if (rebar3Version === null) { if (rebar3Version === null) {
throw new Error( throw new Error(
`Requested rebar3 version (${r3Spec}) not found in version list`, `Requested rebar3 version (${r3Spec}) not found in version list ` +
"(should you be using option 'version-type': 'strict'?)",
) )
} }
@@ -4896,7 +4899,7 @@ async function getElixirVersions() {
.split('\n') .split('\n')
.forEach((line) => { .forEach((line) => {
const elixirMatch = const elixirMatch =
line.match(/^(.+)-otp-([^ ]+)/) || line.match(/^([^ ]+)/) line.match(/^v?(.+)-otp-([^ ]+)/) || line.match(/^v?([^ ]+)/)
const elixirVersion = elixirMatch[1] const elixirVersion = elixirMatch[1]
const otpVersion = elixirMatch[2] const otpVersion = elixirMatch[2]
const otpVersions = otpVersionsForElixirMap.get(elixirVersion) || [] const otpVersions = otpVersionsForElixirMap.get(elixirVersion) || []
@@ -4928,18 +4931,19 @@ async function getRebar3Versions() {
function getVersionFromSpec(spec, versions) { function getVersionFromSpec(spec, versions) {
let version = null let version = null
if (core.getInput('version-type', { required: false }) === 'strict') { if (
spec.match(/rc/) ||
core.getInput('version-type', { required: false }) === 'strict'
) {
version = spec version = spec
} }
if (version === null) { if (version === null) {
// We keep a map of semver => "spec" in order to use semver ranges to find appropriate versions // 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) => { const versionsMap = versions.sort(sortVersions).reduce((acc, v) => {
try { if (!v.match(/rc/)) {
acc[semver.coerce(v).version] = v // release candidates are opt-in
} catch { acc[maybeCoerced(v)] = v
// some stuff can't be coerced, like 'master'
acc[v] = v
} }
return acc return acc
}, {}) }, {})
@@ -4948,13 +4952,26 @@ function getVersionFromSpec(spec, versions) {
version = version =
versionsMap[semver.maxSatisfying(Object.keys(versionsMap), rangeForMax)] versionsMap[semver.maxSatisfying(Object.keys(versionsMap), rangeForMax)]
} else { } else {
version = versionsMap[spec] version = versionsMap[maybeCoerced(spec)]
} }
} }
return version return version
} }
function maybeCoerced(v) {
let ret
try {
ret = semver.coerce(v).version
} catch {
// some stuff can't be coerced, like 'master'
ret = v
}
return ret
}
function sortVersions(left, right) { function sortVersions(left, right) {
let ret = 0 let ret = 0
const newL = verAsComparableStr(left) const newL = verAsComparableStr(left)
+31 -14
View File
@@ -121,7 +121,8 @@ async function getOTPVersion(otpSpec0, osVersion) {
} }
if (otpVersion === null) { if (otpVersion === null) {
throw new Error( throw new Error(
`Requested Erlang/OTP version (${otpSpec0}) not found in version list`, `Requested Erlang/OTP version (${otpSpec0}) not found in version list ` +
"(should you be using option 'version-type': 'strict'?)",
) )
} }
@@ -132,7 +133,7 @@ async function getElixirVersion(exSpec0, otpVersion) {
const elixirVersions = await getElixirVersions() const elixirVersions = await getElixirVersions()
const semverVersions = Array.from(elixirVersions.keys()).sort() const semverVersions = Array.from(elixirVersions.keys()).sort()
const exSpec = exSpec0.match(/^(.+)(-otp-.+)/) || exSpec0.match(/^(.+)/) const exSpec = exSpec0.match(/^v?(.+)(-otp-.+)/) || exSpec0.match(/^v?(.+)/)
let elixirVersion let elixirVersion
if (exSpec[2]) { if (exSpec[2]) {
throw new Error( throw new Error(
@@ -145,7 +146,8 @@ async function getElixirVersion(exSpec0, otpVersion) {
} }
if (!exSpec || elixirVersion === null) { if (!exSpec || elixirVersion === null) {
throw new Error( throw new Error(
`Requested Elixir version (${exSpec0}) not found in version list`, `Requested Elixir version (${exSpec0}) not found in version list ` +
"(should you be using option 'version-type': 'strict'?)",
) )
} }
const otpMatch = otpVersion.match(/^(?:OTP-)?([^.]+)/) const otpMatch = otpVersion.match(/^(?:OTP-)?([^.]+)/)
@@ -168,11 +170,11 @@ async function getElixirVersion(exSpec0, otpVersion) {
} else { } else {
throw new Error( throw new Error(
`Requested Elixir / Erlang/OTP version (${exSpec0} / ${otpVersion}) not ` + `Requested Elixir / Erlang/OTP version (${exSpec0} / ${otpVersion}) not ` +
'found in version list', "found in version list (should you be using option 'version-type': 'strict'?)",
) )
} }
return elixirVersionWithOTP return `v${elixirVersionWithOTP}`
} }
async function getRebar3Version(r3Spec) { async function getRebar3Version(r3Spec) {
@@ -180,7 +182,8 @@ async function getRebar3Version(r3Spec) {
const rebar3Version = getVersionFromSpec(r3Spec, rebar3Versions) const rebar3Version = getVersionFromSpec(r3Spec, rebar3Versions)
if (rebar3Version === null) { if (rebar3Version === null) {
throw new Error( throw new Error(
`Requested rebar3 version (${r3Spec}) not found in version list`, `Requested rebar3 version (${r3Spec}) not found in version list ` +
"(should you be using option 'version-type': 'strict'?)",
) )
} }
@@ -240,7 +243,7 @@ async function getElixirVersions() {
.split('\n') .split('\n')
.forEach((line) => { .forEach((line) => {
const elixirMatch = const elixirMatch =
line.match(/^(.+)-otp-([^ ]+)/) || line.match(/^([^ ]+)/) line.match(/^v?(.+)-otp-([^ ]+)/) || line.match(/^v?([^ ]+)/)
const elixirVersion = elixirMatch[1] const elixirVersion = elixirMatch[1]
const otpVersion = elixirMatch[2] const otpVersion = elixirMatch[2]
const otpVersions = otpVersionsForElixirMap.get(elixirVersion) || [] const otpVersions = otpVersionsForElixirMap.get(elixirVersion) || []
@@ -272,18 +275,19 @@ async function getRebar3Versions() {
function getVersionFromSpec(spec, versions) { function getVersionFromSpec(spec, versions) {
let version = null let version = null
if (core.getInput('version-type', { required: false }) === 'strict') { if (
spec.match(/rc/) ||
core.getInput('version-type', { required: false }) === 'strict'
) {
version = spec version = spec
} }
if (version === null) { if (version === null) {
// We keep a map of semver => "spec" in order to use semver ranges to find appropriate versions // 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) => { const versionsMap = versions.sort(sortVersions).reduce((acc, v) => {
try { if (!v.match(/rc/)) {
acc[semver.coerce(v).version] = v // release candidates are opt-in
} catch { acc[maybeCoerced(v)] = v
// some stuff can't be coerced, like 'master'
acc[v] = v
} }
return acc return acc
}, {}) }, {})
@@ -292,13 +296,26 @@ function getVersionFromSpec(spec, versions) {
version = version =
versionsMap[semver.maxSatisfying(Object.keys(versionsMap), rangeForMax)] versionsMap[semver.maxSatisfying(Object.keys(versionsMap), rangeForMax)]
} else { } else {
version = versionsMap[spec] version = versionsMap[maybeCoerced(spec)]
} }
} }
return version return version
} }
function maybeCoerced(v) {
let ret
try {
ret = semver.coerce(v).version
} catch {
// some stuff can't be coerced, like 'master'
ret = v
}
return ret
}
function sortVersions(left, right) { function sortVersions(left, right) {
let ret = 0 let ret = 0
const newL = verAsComparableStr(left) const newL = verAsComparableStr(left)