Support mise.toml (#415)

* add support for mise.toml version file type

* changes from review

* Keep versioning consistent

* fix default value for version-file-type input

* Use single `version-file`, autodetect type

* Update src/setup-beam.js

Co-authored-by: Paulo F. Oliveira <paulo.ferraz.oliveira@gmail.com>

* Improve log output for mise version files

* Rebuild dist

* Run build-dist

* add support for mise.toml version file type

* changes from review

* Keep versioning consistent

* fix default value for version-file-type input

* Use single `version-file`, autodetect type

* Update src/setup-beam.js

Co-authored-by: Paulo F. Oliveira <paulo.ferraz.oliveira@gmail.com>

* Improve log output for mise version files

* Rebuild dist

* Run build-dist

---------

Co-authored-by: Michael Ruoss <michael.ruoss@cradle.bio>
Co-authored-by: Michael Ruoss <michael@michaelruoss.ch>
Co-authored-by: Paulo F. Oliveira <paulo.ferraz.oliveira@gmail.com>
This commit is contained in:
Dino Kovač
2026-03-09 23:41:15 +01:00
committed by GitHub
parent 0f13bcf18a
commit 98c13a9ed3
8 changed files with 4227 additions and 44 deletions
+1
View File
@@ -1,3 +1,4 @@
/node_modules /node_modules
/test/.tool-versions /test/.tool-versions
/test/mise.toml
/dist/package.json /dist/package.json
+7 -8
View File
@@ -141,23 +141,22 @@ e.g. `${{steps.setup-beam.outputs.erlang-version}}`
### Version file ### Version file
A version file is specified via input `version-file` (e.g.`.tool-versions`). This A version file is specified via input `version-file` (e.g. `.tool-versions` or `mise.toml`).
allows not having to use YML input for versions, though the action does check (and The file type is inferred from the filename: files ending in `.toml` are parsed as
[mise](https://mise.jdx.dev/configuration.html#tools-dev-tools) configuration, and all others
are parsed as [`.tool-versions`](https://asdf-vm.com/manage/configuration.html) (asdf format).
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. 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`, **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. 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, Supported version elements are the same as the ones defined for the YML portion of the action,
with the following correspondence. with the following correspondence.
#### `.tool-versions` format #### `.tool-versions` / `mise.toml` format
| YML | `.tool-versions` | YML | Version file key
|- |- |- |-
| `otp-version` | `erlang` | `otp-version` | `erlang`
| `elixir-version` | `elixir` | `elixir-version` | `elixir`
+3 -1
View File
@@ -69,7 +69,9 @@ inputs:
required: false required: false
version-file: version-file:
description: a versions file (e.g. as used by `asdf`), which defines inputs description: >-
a versions file (e.g. `.tool-versions` as used by `asdf`, or `mise.toml`
as used by `mise`), which defines inputs
default: '' default: ''
required: false required: false
+4112 -16
View File
File diff suppressed because it is too large Load Diff
+8 -1
View File
@@ -12,7 +12,8 @@
"@actions/tool-cache": "4.0.0", "@actions/tool-cache": "4.0.0",
"csv-parse": "6.1.0", "csv-parse": "6.1.0",
"lodash": "4.17.23", "lodash": "4.17.23",
"semver": "7.7.4" "semver": "7.7.4",
"toml": "3.0.0"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "10.0.1", "@eslint/js": "10.0.1",
@@ -3530,6 +3531,12 @@
"url": "https://github.com/sponsors/Borewit" "url": "https://github.com/sponsors/Borewit"
} }
}, },
"node_modules/toml": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz",
"integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==",
"license": "MIT"
},
"node_modules/tslib": { "node_modules/tslib": {
"version": "2.8.1", "version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+2 -1
View File
@@ -22,7 +22,8 @@
"@actions/tool-cache": "4.0.0", "@actions/tool-cache": "4.0.0",
"csv-parse": "6.1.0", "csv-parse": "6.1.0",
"lodash": "4.17.23", "lodash": "4.17.23",
"semver": "7.7.4" "semver": "7.7.4",
"toml": "3.0.0"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "10.0.1", "@eslint/js": "10.0.1",
+44 -16
View File
@@ -8,10 +8,12 @@ import * as tc from '@actions/tool-cache'
import * as semver from 'semver' import * as semver from 'semver'
import * as csv from 'csv-parse/sync' import * as csv from 'csv-parse/sync'
import _ from 'lodash' import _ from 'lodash'
import toml from 'toml'
const __dirname = path.dirname(fileURLToPath(import.meta.url)) const __dirname = path.dirname(fileURLToPath(import.meta.url))
const MAX_HTTP_RETRIES = 3 const MAX_HTTP_RETRIES = 3
const APPS = ['erlang', 'elixir', 'gleam', 'rebar']
if (process.env.NODE_ENV !== 'test') { if (process.env.NODE_ENV !== 'test') {
main().catch((err) => { main().catch((err) => {
@@ -802,28 +804,14 @@ alongside ${alternativeName}=${alternativeValue} \
return input return input
} }
function parseVersionFile(versionFilePath0) { function parseToolVersionsFile(versionFilePath) {
const versionFilePath = path.resolve(
process.env.GITHUB_WORKSPACE,
versionFilePath0,
)
if (!fs.existsSync(versionFilePath)) {
throw new Error(
`The specified version file, ${versionFilePath0}, does not exist`,
)
}
core.startGroup(`Parsing version file at ${versionFilePath0}`)
const appVersions = new Map() const appVersions = new Map()
const versions = fs.readFileSync(versionFilePath, 'utf8') 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(/\r?\n/).forEach((line) => { versions.split(/\r?\n/).forEach((line) => {
const appVersion = line.match(/^([^ ]+)[ ]+(ref:v?)?([^ #]+)/) const appVersion = line.match(/^([^ ]+)[ ]+(ref:v?)?([^ #]+)/)
if (appVersion) { if (appVersion) {
const app = appVersion[1] const app = appVersion[1]
if (['erlang', 'elixir', 'gleam', 'rebar'].includes(app)) { if (APPS.includes(app)) {
const [, , , version] = appVersion const [, , , version] = appVersion
core.info(`Consuming ${app} at version ${version}`) core.info(`Consuming ${app} at version ${version}`)
appVersions.set(app, version) appVersions.set(app, version)
@@ -840,6 +828,46 @@ function parseVersionFile(versionFilePath0) {
return appVersions return appVersions
} }
function parseMiseTomlFile(versionFilePath) {
const appVersions = new Map()
const miseToml = fs.readFileSync(versionFilePath, 'utf8')
const miseTomlParsed = toml.parse(miseToml)
for (const app in miseTomlParsed.tools) {
if (APPS.includes(app)) {
const appVersion = miseTomlParsed.tools[app]
const version =
typeof appVersion == 'object' ? appVersion.version : appVersion
core.info(`Consuming ${app} at version ${version}`)
appVersions.set(app, version)
}
}
return appVersions
}
function parseVersionFile(versionFilePath0) {
const versionFilePath = path.resolve(
process.env.GITHUB_WORKSPACE,
versionFilePath0,
)
if (!fs.existsSync(versionFilePath)) {
throw new Error(
`The specified version file, ${versionFilePath0}, does not exist`,
)
}
if (versionFilePath0.endsWith('.toml')) {
core.startGroup(
`Parsing ${path.basename(versionFilePath0)} file at ${versionFilePath0}`,
)
return parseMiseTomlFile(versionFilePath)
}
core.startGroup(`Parsing .tool-versions file at ${versionFilePath0}`)
return parseToolVersionsFile(versionFilePath)
}
function debugLog(groupName, message) { function debugLog(groupName, message) {
const group = `Debugging for ${groupName}` const group = `Debugging for ${groupName}`
core.debug( core.debug(
+50 -1
View File
@@ -969,7 +969,7 @@ describe('.getVersionFromSpec(_)', () => {
}) })
}) })
describe('version file', () => { describe('.tool-versions file', () => {
const otpVersion = unsimulateInput('otp-version') const otpVersion = unsimulateInput('otp-version')
const elixirVersion = unsimulateInput('elixir-version') const elixirVersion = unsimulateInput('elixir-version')
const gleamVersion = unsimulateInput('gleam-version') const gleamVersion = unsimulateInput('gleam-version')
@@ -1018,6 +1018,55 @@ gleam ${gleam} \n`
simulateInput('gleam-version', gleamVersion) simulateInput('gleam-version', gleamVersion)
}) })
describe('mise.toml file', () => {
const otpVersion = unsimulateInput('otp-version')
const elixirVersion = unsimulateInput('elixir-version')
const gleamVersion = unsimulateInput('gleam-version')
it('is parsed correctly', async () => {
const erlang = '27.3.4.1'
const elixir = '1.18.4'
const gleam = '1.9.1'
let miseToml = `[tools]
"erlang" = "${erlang}"
elixir = { version = "${elixir}", postinstall="mix deps.get" } # comment, with space and ref:
"not-gleam" = 0.23 # not picked up
"gleam" = "${gleam}" \n`
const filename = 'test/mise.toml'
if (process.platform === 'win32') {
// Force \r\n to test in Windows
miseToml = miseToml.replace(/\n/g, '\r\n')
}
fs.writeFileSync(filename, miseToml)
process.env.GITHUB_WORKSPACE = ''
const appVersions = setupBeam.parseVersionFile(filename)
assert.strictEqual(appVersions.get('erlang'), erlang)
assert.strictEqual(appVersions.get('elixir'), elixir)
const absoluteFilename = path.join(os.tmpdir(), 'mise.toml')
fs.writeFileSync(absoluteFilename, miseToml)
process.env.GITHUB_WORKSPACE = process.cwd()
const absoluteAppVersions = setupBeam.parseVersionFile(absoluteFilename)
assert.strictEqual(absoluteAppVersions.get('erlang'), erlang)
assert.strictEqual(absoluteAppVersions.get('elixir'), elixir)
assert.ok(async () => {
await setupBeam.install('otp', { toolVersion: erlang })
})
assert.ok(async () => {
await setupBeam.install('elixir', { toolVersion: elixir })
})
assert.ok(async () => {
await setupBeam.install('gleam', { toolVersion: gleam })
})
})
simulateInput('otp-version', otpVersion)
simulateInput('elixir-version', elixirVersion)
simulateInput('gleam-version', gleamVersion)
})
describe('.get(_)', () => { describe('.get(_)', () => {
it('retries as expected', async () => { it('retries as expected', async () => {
let attempt = 0 let attempt = 0