mirror of
https://github.com/erlef/setup-beam.git
synced 2026-07-22 22:56:07 +00:00
Ease consumption by means of .tool-versions (#159)
* Ease consumption by means of .tool-versions * Update as per self-review * Update as per self-review * Act on CI results * Act on CI results * Act on CI results This value is check by code, in any case * Act on CI results * Act on CI results * Act on CI results It appears we can't install Elixir twice on Windows, but can on Ubuntu; this is outside the scope of this branch's context so I won't fix it now * Tweak visual consumption elements * Ease potential future maintenance and/or update
This commit is contained in:
committed by
GitHub
parent
529221db21
commit
c373088147
@@ -40,6 +40,12 @@ jobs:
|
|||||||
node-version: '14'
|
node-version: '14'
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm test
|
- run: npm test
|
||||||
|
- name: .tool-versions test
|
||||||
|
id: setup-beam
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
version-file: __tests__/.tool-versions
|
||||||
|
version-type: strict
|
||||||
|
|
||||||
unit_tests_windows:
|
unit_tests_windows:
|
||||||
name: Unit tests (Windows)
|
name: Unit tests (Windows)
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
node_modules/**
|
node_modules/**
|
||||||
dist/.github/workflows
|
dist/.github/workflows
|
||||||
|
__tests__/.tool-versions
|
||||||
|
|||||||
@@ -16,9 +16,11 @@ workflow by:
|
|||||||
- optionally, installing [Gleam](https://gleam.run/)
|
- optionally, installing [Gleam](https://gleam.run/)
|
||||||
- optionally, installing [`rebar3`](https://www.rebar3.org/)
|
- optionally, installing [`rebar3`](https://www.rebar3.org/)
|
||||||
- optionally, installing [`hex`](https://hex.pm/)
|
- optionally, installing [`hex`](https://hex.pm/)
|
||||||
|
- optionally, opting for strict or loose version matching
|
||||||
- optionally, having
|
- optionally, having
|
||||||
[problem matchers](https://github.com/actions/toolkit/blob/main/docs/problem-matchers.md) show
|
[problem matchers](https://github.com/actions/toolkit/blob/main/docs/problem-matchers.md) show
|
||||||
warnings and errors on pull requests
|
warnings and errors on pull requests
|
||||||
|
- optionally, using a version file (as explained in "Version file", below), to identify versions
|
||||||
|
|
||||||
**Note**: currently, this action only supports Actions' `ubuntu-` and `windows-` runtimes.
|
**Note**: currently, this action only supports Actions' `ubuntu-` and `windows-` runtimes.
|
||||||
|
|
||||||
@@ -86,6 +88,31 @@ jobs:
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Version file
|
||||||
|
|
||||||
|
A version file is specified via input `version-file` (e.g.`.tool-versions`). This
|
||||||
|
allows not having to use YML input for versions, though the action does check (and
|
||||||
|
will exit with error) if both inputs are set.
|
||||||
|
|
||||||
|
**Note**: if you're using a version file, option `version-type` is checked to be `strict`,
|
||||||
|
and will make the action exit with error otherwise.
|
||||||
|
|
||||||
|
The following version file formats are supported:
|
||||||
|
|
||||||
|
- `.tool-versions`, as specified by [asdf: Configuration](https://asdf-vm.com/manage/configuration.html)
|
||||||
|
|
||||||
|
Supported version elements are the same as the ones defined for the YML portion of the action,
|
||||||
|
with the following correspondence.
|
||||||
|
|
||||||
|
#### `.tool-versions` format
|
||||||
|
|
||||||
|
| YML | `.tool-versions` |
|
||||||
|
|- |-
|
||||||
|
| `otp-version` | `erlang`
|
||||||
|
| `elixir-version` | `elixir`
|
||||||
|
| `gleam-version` | `gleam`
|
||||||
|
| `rebar3-version` | `rebar`
|
||||||
|
|
||||||
### Example (Erlang/OTP + Elixir, on Ubuntu)
|
### Example (Erlang/OTP + Elixir, on Ubuntu)
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ simulateInput('install-rebar', 'true')
|
|||||||
simulateInput('install-hex', 'true')
|
simulateInput('install-hex', 'true')
|
||||||
|
|
||||||
const assert = require('assert')
|
const assert = require('assert')
|
||||||
|
const fs = require('fs')
|
||||||
const setupBeam = require('../src/setup-beam')
|
const setupBeam = require('../src/setup-beam')
|
||||||
const installer = require('../src/installer')
|
const installer = require('../src/installer')
|
||||||
|
|
||||||
@@ -20,6 +21,8 @@ async function all() {
|
|||||||
await testRebar3Versions()
|
await testRebar3Versions()
|
||||||
|
|
||||||
await testGetVersionFromSpec()
|
await testGetVersionFromSpec()
|
||||||
|
|
||||||
|
await testParseVersionFile()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testFailInstallOTP() {
|
async function testFailInstallOTP() {
|
||||||
@@ -405,8 +408,39 @@ async function testGetVersionFromSpec() {
|
|||||||
simulateInput('version-type', 'loose')
|
simulateInput('version-type', 'loose')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function testParseVersionFile() {
|
||||||
|
const otpVersion = unsimulateInput('otp-version')
|
||||||
|
const elixirVersion = unsimulateInput('elixir-version')
|
||||||
|
|
||||||
|
const erlang = '25.1.1'
|
||||||
|
const elixir = '1.14.1'
|
||||||
|
const toolVersions = `# a comment
|
||||||
|
erlang ${erlang}# comment, no space
|
||||||
|
elixir ${elixir} # comment, with space
|
||||||
|
gleam 0.23 # not picked up`
|
||||||
|
const filename = '__tests__/.tool-versions'
|
||||||
|
fs.writeFileSync(filename, toolVersions)
|
||||||
|
process.env.GITHUB_WORKSPACE = ''
|
||||||
|
const appVersions = setupBeam.parseVersionFile(filename)
|
||||||
|
assert.strictEqual(appVersions.get('erlang'), erlang)
|
||||||
|
assert.strictEqual(appVersions.get('elixir'), elixir)
|
||||||
|
|
||||||
|
simulateInput('otp-version', otpVersion)
|
||||||
|
simulateInput('elixir-version', elixirVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
function unsimulateInput(key) {
|
||||||
|
const before = process.env[input(key)]
|
||||||
|
simulateInput(key, '')
|
||||||
|
return before
|
||||||
|
}
|
||||||
|
|
||||||
function simulateInput(key, value) {
|
function simulateInput(key, value) {
|
||||||
process.env[`INPUT_${key.replace(/ /g, '_').toUpperCase()}`] = value
|
process.env[input(key)] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
function input(key) {
|
||||||
|
return `INPUT_${key.replace(/ /g, '_').toUpperCase()}`
|
||||||
}
|
}
|
||||||
|
|
||||||
all()
|
all()
|
||||||
|
|||||||
+5
-2
@@ -11,7 +11,6 @@ inputs:
|
|||||||
otp-version:
|
otp-version:
|
||||||
description: Version range or exact version of Erlang/OTP to use,
|
description: Version range or exact version of Erlang/OTP to use,
|
||||||
or false when installing only Gleam without OTP
|
or false when installing only Gleam without OTP
|
||||||
required: true
|
|
||||||
elixir-version:
|
elixir-version:
|
||||||
description: Version range or exact version of Elixir to use
|
description: Version range or exact version of Elixir to use
|
||||||
gleam-version:
|
gleam-version:
|
||||||
@@ -31,8 +30,12 @@ inputs:
|
|||||||
to guess versions based on semver rules
|
to guess versions based on semver rules
|
||||||
default: loose
|
default: loose
|
||||||
disable_problem_matchers:
|
disable_problem_matchers:
|
||||||
description: whether to have the problem matchers present in the results (or not)
|
description: whether to have the problem matchers present in the results
|
||||||
|
(or not)
|
||||||
default: false
|
default: false
|
||||||
|
version-file:
|
||||||
|
description: a versions file (e.g. as used by `asdf`), which defines inputs
|
||||||
|
default: ''
|
||||||
outputs:
|
outputs:
|
||||||
elixir-version:
|
elixir-version:
|
||||||
description: Exact version of Elixir that was installed
|
description: Exact version of Elixir that was installed
|
||||||
|
|||||||
Vendored
+76
-14
@@ -7196,6 +7196,7 @@ const { exec } = __nccwpck_require__(1514)
|
|||||||
const path = __nccwpck_require__(1017)
|
const path = __nccwpck_require__(1017)
|
||||||
const semver = __nccwpck_require__(1383)
|
const semver = __nccwpck_require__(1383)
|
||||||
const https = __nccwpck_require__(5687)
|
const https = __nccwpck_require__(5687)
|
||||||
|
const fs = __nccwpck_require__(7147)
|
||||||
const installer = __nccwpck_require__(2127)
|
const installer = __nccwpck_require__(2127)
|
||||||
|
|
||||||
main().catch((err) => {
|
main().catch((err) => {
|
||||||
@@ -7205,24 +7206,31 @@ main().catch((err) => {
|
|||||||
async function main() {
|
async function main() {
|
||||||
installer.checkPlatform()
|
installer.checkPlatform()
|
||||||
|
|
||||||
|
const versionFilePath = getInput('version-file', false)
|
||||||
|
let versions
|
||||||
|
if (versionFilePath) {
|
||||||
|
if (!isStrictVersion()) {
|
||||||
|
throw new Error(
|
||||||
|
"you have to set version-type=strict if you're using version-file",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
versions = parseVersionFile(versionFilePath)
|
||||||
|
}
|
||||||
|
|
||||||
const osVersion = getRunnerOSVersion()
|
const osVersion = getRunnerOSVersion()
|
||||||
const otpSpec = core.getInput('otp-version', { required: true })
|
const otpSpec = getInput('otp-version', true, 'erlang', versions)
|
||||||
const elixirSpec = core.getInput('elixir-version', { required: false })
|
const elixirSpec = getInput('elixir-version', false, 'elixir', versions)
|
||||||
const gleamSpec = core.getInput('gleam-version', { required: false })
|
const gleamSpec = getInput('gleam-version', false, 'gleam', versions)
|
||||||
const rebar3Spec = core.getInput('rebar3-version', { required: false })
|
const rebar3Spec = getInput('rebar3-version', false, 'rebar', versions)
|
||||||
|
|
||||||
if (otpSpec !== 'false') {
|
if (otpSpec !== 'false') {
|
||||||
await installOTP(otpSpec, osVersion)
|
await installOTP(otpSpec, osVersion)
|
||||||
const elixirInstalled = await maybeInstallElixir(elixirSpec, otpSpec)
|
const elixirInstalled = await maybeInstallElixir(elixirSpec, otpSpec)
|
||||||
|
|
||||||
if (elixirInstalled === true) {
|
if (elixirInstalled === true) {
|
||||||
const shouldMixRebar = core.getInput('install-rebar', {
|
const shouldMixRebar = getInput('install-rebar', false)
|
||||||
required: false,
|
|
||||||
})
|
|
||||||
await mix(shouldMixRebar, 'rebar')
|
await mix(shouldMixRebar, 'rebar')
|
||||||
const shouldMixHex = core.getInput('install-hex', {
|
const shouldMixHex = getInput('install-hex', false)
|
||||||
required: false,
|
|
||||||
})
|
|
||||||
await mix(shouldMixHex, 'hex')
|
await mix(shouldMixHex, 'hex')
|
||||||
}
|
}
|
||||||
} else if (!gleamSpec) {
|
} else if (!gleamSpec) {
|
||||||
@@ -7254,9 +7262,7 @@ async function maybeInstallElixir(elixirSpec, otpVersion) {
|
|||||||
console.log(`##[group]Installing Elixir ${elixirVersion}`)
|
console.log(`##[group]Installing Elixir ${elixirVersion}`)
|
||||||
await installer.installElixir(elixirVersion)
|
await installer.installElixir(elixirVersion)
|
||||||
core.setOutput('elixir-version', elixirVersion)
|
core.setOutput('elixir-version', elixirVersion)
|
||||||
const disableProblemMatchers = core.getInput('disable_problem_matchers', {
|
const disableProblemMatchers = getInput('disable_problem_matchers', false)
|
||||||
required: false,
|
|
||||||
})
|
|
||||||
if (disableProblemMatchers === 'false') {
|
if (disableProblemMatchers === 'false') {
|
||||||
const matchersPath = __nccwpck_require__.ab + ".github"
|
const matchersPath = __nccwpck_require__.ab + ".github"
|
||||||
console.log(
|
console.log(
|
||||||
@@ -7503,7 +7509,7 @@ async function getRebar3Versions() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isStrictVersion() {
|
function isStrictVersion() {
|
||||||
return core.getInput('version-type', { required: false }) === 'strict'
|
return getInput('version-type', false) === 'strict'
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVersionFromSpec(spec, versions, maybePrependWithV0) {
|
function getVersionFromSpec(spec, versions, maybePrependWithV0) {
|
||||||
@@ -7653,12 +7659,68 @@ function isVersion(v) {
|
|||||||
return /^v?\d+/.test(v)
|
return /^v?\d+/.test(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getInput(inputName, required, alternativeName, alternatives) {
|
||||||
|
const alternativeValue = (alternatives || new Map()).get(alternativeName)
|
||||||
|
let input = core.getInput(inputName, {
|
||||||
|
required: alternativeValue ? false : required,
|
||||||
|
})
|
||||||
|
// We can't have both input and alternativeValue set
|
||||||
|
if (input && alternativeValue) {
|
||||||
|
throw new Error(
|
||||||
|
`Found input ${inputName}=${input} (from the YML) \
|
||||||
|
alongside ${alternativeName}=${alternativeValue} \
|
||||||
|
(from the version file). You must choose one or the other.`,
|
||||||
|
)
|
||||||
|
} else if (!input) {
|
||||||
|
input = alternativeValue
|
||||||
|
}
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseVersionFile(versionFilePath0) {
|
||||||
|
const versionFilePath = path.join(
|
||||||
|
process.env.GITHUB_WORKSPACE,
|
||||||
|
versionFilePath0,
|
||||||
|
)
|
||||||
|
if (!fs.existsSync(versionFilePath)) {
|
||||||
|
throw new Error(
|
||||||
|
`The specified version file, ${versionFilePath0}, does not exist`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
console.log(`##[group]Parsing version file at ${versionFilePath0}`)
|
||||||
|
const appVersions = new Map()
|
||||||
|
const versions = fs.readFileSync(versionFilePath, 'utf8')
|
||||||
|
// For the time being we parse .tool-versions
|
||||||
|
// If we ever start parsing something else, this should
|
||||||
|
// become default in a new option named e.g. version-file-type
|
||||||
|
versions.split('\n').forEach((line) => {
|
||||||
|
const appVersion = line.match(/^([^ ]+)[ ]+([^ #]+)/)
|
||||||
|
if (appVersion) {
|
||||||
|
const app = appVersion[1]
|
||||||
|
if (['erlang', 'elixir', 'gleam', 'rebar'].includes(app)) {
|
||||||
|
const [, , version] = appVersion
|
||||||
|
console.log(`Consuming ${app} at version ${version}`)
|
||||||
|
appVersions.set(app, version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (!appVersions.size) {
|
||||||
|
console.log('There was apparently nothing to consume')
|
||||||
|
} else {
|
||||||
|
console.log('... done!')
|
||||||
|
}
|
||||||
|
console.log('##[endgroup]')
|
||||||
|
|
||||||
|
return appVersions
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getOTPVersion,
|
getOTPVersion,
|
||||||
getElixirVersion,
|
getElixirVersion,
|
||||||
getGleamVersion,
|
getGleamVersion,
|
||||||
getRebar3Version,
|
getRebar3Version,
|
||||||
getVersionFromSpec,
|
getVersionFromSpec,
|
||||||
|
parseVersionFile,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+76
-14
@@ -3,6 +3,7 @@ const { exec } = require('@actions/exec')
|
|||||||
const path = require('path')
|
const path = require('path')
|
||||||
const semver = require('semver')
|
const semver = require('semver')
|
||||||
const https = require('https')
|
const https = require('https')
|
||||||
|
const fs = require('fs')
|
||||||
const installer = require('./installer')
|
const installer = require('./installer')
|
||||||
|
|
||||||
main().catch((err) => {
|
main().catch((err) => {
|
||||||
@@ -12,24 +13,31 @@ main().catch((err) => {
|
|||||||
async function main() {
|
async function main() {
|
||||||
installer.checkPlatform()
|
installer.checkPlatform()
|
||||||
|
|
||||||
|
const versionFilePath = getInput('version-file', false)
|
||||||
|
let versions
|
||||||
|
if (versionFilePath) {
|
||||||
|
if (!isStrictVersion()) {
|
||||||
|
throw new Error(
|
||||||
|
"you have to set version-type=strict if you're using version-file",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
versions = parseVersionFile(versionFilePath)
|
||||||
|
}
|
||||||
|
|
||||||
const osVersion = getRunnerOSVersion()
|
const osVersion = getRunnerOSVersion()
|
||||||
const otpSpec = core.getInput('otp-version', { required: true })
|
const otpSpec = getInput('otp-version', true, 'erlang', versions)
|
||||||
const elixirSpec = core.getInput('elixir-version', { required: false })
|
const elixirSpec = getInput('elixir-version', false, 'elixir', versions)
|
||||||
const gleamSpec = core.getInput('gleam-version', { required: false })
|
const gleamSpec = getInput('gleam-version', false, 'gleam', versions)
|
||||||
const rebar3Spec = core.getInput('rebar3-version', { required: false })
|
const rebar3Spec = getInput('rebar3-version', false, 'rebar', versions)
|
||||||
|
|
||||||
if (otpSpec !== 'false') {
|
if (otpSpec !== 'false') {
|
||||||
await installOTP(otpSpec, osVersion)
|
await installOTP(otpSpec, osVersion)
|
||||||
const elixirInstalled = await maybeInstallElixir(elixirSpec, otpSpec)
|
const elixirInstalled = await maybeInstallElixir(elixirSpec, otpSpec)
|
||||||
|
|
||||||
if (elixirInstalled === true) {
|
if (elixirInstalled === true) {
|
||||||
const shouldMixRebar = core.getInput('install-rebar', {
|
const shouldMixRebar = getInput('install-rebar', false)
|
||||||
required: false,
|
|
||||||
})
|
|
||||||
await mix(shouldMixRebar, 'rebar')
|
await mix(shouldMixRebar, 'rebar')
|
||||||
const shouldMixHex = core.getInput('install-hex', {
|
const shouldMixHex = getInput('install-hex', false)
|
||||||
required: false,
|
|
||||||
})
|
|
||||||
await mix(shouldMixHex, 'hex')
|
await mix(shouldMixHex, 'hex')
|
||||||
}
|
}
|
||||||
} else if (!gleamSpec) {
|
} else if (!gleamSpec) {
|
||||||
@@ -61,9 +69,7 @@ async function maybeInstallElixir(elixirSpec, otpVersion) {
|
|||||||
console.log(`##[group]Installing Elixir ${elixirVersion}`)
|
console.log(`##[group]Installing Elixir ${elixirVersion}`)
|
||||||
await installer.installElixir(elixirVersion)
|
await installer.installElixir(elixirVersion)
|
||||||
core.setOutput('elixir-version', elixirVersion)
|
core.setOutput('elixir-version', elixirVersion)
|
||||||
const disableProblemMatchers = core.getInput('disable_problem_matchers', {
|
const disableProblemMatchers = getInput('disable_problem_matchers', false)
|
||||||
required: false,
|
|
||||||
})
|
|
||||||
if (disableProblemMatchers === 'false') {
|
if (disableProblemMatchers === 'false') {
|
||||||
const matchersPath = path.join(__dirname, '..', '.github')
|
const matchersPath = path.join(__dirname, '..', '.github')
|
||||||
console.log(
|
console.log(
|
||||||
@@ -310,7 +316,7 @@ async function getRebar3Versions() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isStrictVersion() {
|
function isStrictVersion() {
|
||||||
return core.getInput('version-type', { required: false }) === 'strict'
|
return getInput('version-type', false) === 'strict'
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVersionFromSpec(spec, versions, maybePrependWithV0) {
|
function getVersionFromSpec(spec, versions, maybePrependWithV0) {
|
||||||
@@ -460,10 +466,66 @@ function isVersion(v) {
|
|||||||
return /^v?\d+/.test(v)
|
return /^v?\d+/.test(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getInput(inputName, required, alternativeName, alternatives) {
|
||||||
|
const alternativeValue = (alternatives || new Map()).get(alternativeName)
|
||||||
|
let input = core.getInput(inputName, {
|
||||||
|
required: alternativeValue ? false : required,
|
||||||
|
})
|
||||||
|
// We can't have both input and alternativeValue set
|
||||||
|
if (input && alternativeValue) {
|
||||||
|
throw new Error(
|
||||||
|
`Found input ${inputName}=${input} (from the YML) \
|
||||||
|
alongside ${alternativeName}=${alternativeValue} \
|
||||||
|
(from the version file). You must choose one or the other.`,
|
||||||
|
)
|
||||||
|
} else if (!input) {
|
||||||
|
input = alternativeValue
|
||||||
|
}
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseVersionFile(versionFilePath0) {
|
||||||
|
const versionFilePath = path.join(
|
||||||
|
process.env.GITHUB_WORKSPACE,
|
||||||
|
versionFilePath0,
|
||||||
|
)
|
||||||
|
if (!fs.existsSync(versionFilePath)) {
|
||||||
|
throw new Error(
|
||||||
|
`The specified version file, ${versionFilePath0}, does not exist`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
console.log(`##[group]Parsing version file at ${versionFilePath0}`)
|
||||||
|
const appVersions = new Map()
|
||||||
|
const versions = fs.readFileSync(versionFilePath, 'utf8')
|
||||||
|
// For the time being we parse .tool-versions
|
||||||
|
// If we ever start parsing something else, this should
|
||||||
|
// become default in a new option named e.g. version-file-type
|
||||||
|
versions.split('\n').forEach((line) => {
|
||||||
|
const appVersion = line.match(/^([^ ]+)[ ]+([^ #]+)/)
|
||||||
|
if (appVersion) {
|
||||||
|
const app = appVersion[1]
|
||||||
|
if (['erlang', 'elixir', 'gleam', 'rebar'].includes(app)) {
|
||||||
|
const [, , version] = appVersion
|
||||||
|
console.log(`Consuming ${app} at version ${version}`)
|
||||||
|
appVersions.set(app, version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (!appVersions.size) {
|
||||||
|
console.log('There was apparently nothing to consume')
|
||||||
|
} else {
|
||||||
|
console.log('... done!')
|
||||||
|
}
|
||||||
|
console.log('##[endgroup]')
|
||||||
|
|
||||||
|
return appVersions
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getOTPVersion,
|
getOTPVersion,
|
||||||
getElixirVersion,
|
getElixirVersion,
|
||||||
getGleamVersion,
|
getGleamVersion,
|
||||||
getRebar3Version,
|
getRebar3Version,
|
||||||
getVersionFromSpec,
|
getVersionFromSpec,
|
||||||
|
parseVersionFile,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user