mirror of
https://github.com/erlef/setup-beam.git
synced 2026-07-23 07:06:07 +00:00
Rework our version matching algorithm (#217)
* Update some dependencies' versions (also, get rid of husky) * Introduce branch dependency execution We want the most important things to fail first and prevent the other ones from executing e.g. if your dist/ is not updated, there's no point seeing the tests fail * Rid ourselves of bloat * Introduce expectations for upcoming version * Tweak whitespace * Tweak our calls to getVersionFromSpec (increase code consistency) * Remove non-used variable * Start coercing on input, stop sorting/maybe prepending 'v' on input We'll deal with all this in a centralized place to make it easier to reason on the code * Revamp our getVersionFromSpec 1. stop accepting maybePrependWithV0, since this is only used in specialized cases 2. sort versions internally (don't expect it to come sorted) 3. compare rc/strict and .includes in a single go (don't separate these) 4. don't expect already coerced version arrays, coerce them when required (the maybeCoerced version had changed already, which means we're smarter at putting stuff inside the version array) 5. raise an exception if a semver-invalid version is not flagged as strict * Reduce our expectations, but still be appropriate to our mission * Update our tests for our new expectations (start the TDD bit) * Execute npm run build-dist and keep results * Increase inter-job dependency * Don't expect markdownlint to be installed remotely * Revert "Increase inter-job dependency" This reverts commit 500b0fe7e9eec1c4f258b4d16b40719ee1d0a1ad. * Revert "Introduce branch dependency execution" This reverts commit fbe1317a800d7722f4e8b668c073218263d0ab17. * Have all the CI workflows run under the same conditions * Fix as per CI results * Re-establish the possibility to match versions like 22.3.4.2.1.0 We cheat on the semver matching: 1. we keep an ordered list of versions that respect e.g. 22.3.4 2. if we match 22.3.4 when fetching a version, we get the last value from the list of versions that respect it, e.g. 22.3.4.2.1.0 (since this is ordered, it should work as previously expected) This increases code complexity for getVersionFromSpec, but hopefully not by much... We also remove throw-based test cases (non-versions will fail later, with null), and add some tests to prove our need code. * Trim out linting practices But still keep them useful
This commit is contained in:
committed by
GitHub
parent
efc884ac2f
commit
f6a73a7bf1
+5
-8
@@ -1,19 +1,16 @@
|
||||
---
|
||||
env:
|
||||
node: true
|
||||
es2021: true
|
||||
es2022: true
|
||||
extends:
|
||||
- airbnb
|
||||
- eslint:recommended
|
||||
parserOptions:
|
||||
ecmaVersion: 12
|
||||
ecmaVersion: 2022
|
||||
rules:
|
||||
indent: ["warn", 2]
|
||||
max-len: ["warn", 100]
|
||||
no-undef: "error"
|
||||
no-unused-vars: ["error", {"varsIgnorePattern": "^_"}]
|
||||
indent: [warn, 2]
|
||||
max-len: [warn, 100]
|
||||
no-use-before-define: 0
|
||||
operator-linebreak: 0
|
||||
require-jsdoc: 0
|
||||
semi: 0
|
||||
|
||||
settings:
|
||||
|
||||
@@ -6,7 +6,8 @@ on:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
@@ -19,16 +20,8 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14'
|
||||
- run: npm install -g npm
|
||||
- run: npm install
|
||||
- run: npm run build
|
||||
- run: npm run format
|
||||
- run: npm install -g markdownlint-cli
|
||||
- run: npm run markdownlint
|
||||
- run: npm run shellcheck
|
||||
- run: npm run yamllint
|
||||
- run: npm run jslint
|
||||
node-version: '16'
|
||||
- run: npm run build-dist
|
||||
- name: Check if build left artifacts
|
||||
run: git diff --exit-code --ignore-space-at-eol
|
||||
|
||||
@@ -39,7 +32,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14'
|
||||
node-version: '16'
|
||||
- run: npm ci
|
||||
- run: npm test
|
||||
- name: .tool-versions test
|
||||
@@ -56,6 +49,6 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14'
|
||||
node-version: '16'
|
||||
- run: npm install --production
|
||||
- run: npm test
|
||||
|
||||
@@ -6,7 +6,8 @@ on:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
integration_test:
|
||||
@@ -111,12 +112,10 @@ jobs:
|
||||
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'
|
||||
- elixir-version: 'main'
|
||||
otp-version: '23.1'
|
||||
os: 'ubuntu-20.04'
|
||||
version-type: 'strict'
|
||||
- elixir-version: 'main'
|
||||
otp-version: '25'
|
||||
os: 'ubuntu-20.04'
|
||||
|
||||
@@ -12,9 +12,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14'
|
||||
- run:
|
||||
./.github/workflows/update_3rd_party_licenses.sh
|
||||
env:
|
||||
|
||||
@@ -6,7 +6,8 @@ on:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
integration_test:
|
||||
|
||||
Vendored
+179
-96
@@ -6221,15 +6221,18 @@ class Range {
|
||||
const hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE]
|
||||
range = range.replace(hr, hyphenReplace(this.options.includePrerelease))
|
||||
debug('hyphen replace', range)
|
||||
|
||||
// `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`
|
||||
range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace)
|
||||
debug('comparator trim', range)
|
||||
|
||||
// `~ 1.2.3` => `~1.2.3`
|
||||
range = range.replace(re[t.TILDETRIM], tildeTrimReplace)
|
||||
debug('tilde trim', range)
|
||||
|
||||
// `^ 1.2.3` => `^1.2.3`
|
||||
range = range.replace(re[t.CARETTRIM], caretTrimReplace)
|
||||
debug('caret trim', range)
|
||||
|
||||
// At this point, the range is completely trimmed and
|
||||
// ready to be split into comparators.
|
||||
@@ -7531,6 +7534,10 @@ const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER ||
|
||||
// Max safe segment length for coercion.
|
||||
const MAX_SAFE_COMPONENT_LENGTH = 16
|
||||
|
||||
// Max safe length for a build identifier. The max length minus 6 characters for
|
||||
// the shortest version with a build 0.0.0+BUILD.
|
||||
const MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6
|
||||
|
||||
const RELEASE_TYPES = [
|
||||
'major',
|
||||
'premajor',
|
||||
@@ -7544,6 +7551,7 @@ const RELEASE_TYPES = [
|
||||
module.exports = {
|
||||
MAX_LENGTH,
|
||||
MAX_SAFE_COMPONENT_LENGTH,
|
||||
MAX_SAFE_BUILD_LENGTH,
|
||||
MAX_SAFE_INTEGER,
|
||||
RELEASE_TYPES,
|
||||
SEMVER_SPEC_VERSION,
|
||||
@@ -7625,7 +7633,7 @@ module.exports = parseOptions
|
||||
/***/ 9523:
|
||||
/***/ ((module, exports, __nccwpck_require__) => {
|
||||
|
||||
const { MAX_SAFE_COMPONENT_LENGTH } = __nccwpck_require__(2293)
|
||||
const { MAX_SAFE_COMPONENT_LENGTH, MAX_SAFE_BUILD_LENGTH } = __nccwpck_require__(2293)
|
||||
const debug = __nccwpck_require__(427)
|
||||
exports = module.exports = {}
|
||||
|
||||
@@ -7636,16 +7644,31 @@ const src = exports.src = []
|
||||
const t = exports.t = {}
|
||||
let R = 0
|
||||
|
||||
const LETTERDASHNUMBER = '[a-zA-Z0-9-]'
|
||||
|
||||
// Replace some greedy regex tokens to prevent regex dos issues. These regex are
|
||||
// used internally via the safeRe object since all inputs in this library get
|
||||
// normalized first to trim and collapse all extra whitespace. The original
|
||||
// regexes are exported for userland consumption and lower level usage. A
|
||||
// future breaking change could export the safer regex only with a note that
|
||||
// all input should have extra whitespace removed.
|
||||
const safeRegexReplacements = [
|
||||
['\\s', 1],
|
||||
['\\d', MAX_SAFE_COMPONENT_LENGTH],
|
||||
[LETTERDASHNUMBER, MAX_SAFE_BUILD_LENGTH],
|
||||
]
|
||||
|
||||
const makeSafeRegex = (value) => {
|
||||
for (const [token, max] of safeRegexReplacements) {
|
||||
value = value
|
||||
.split(`${token}*`).join(`${token}{0,${max}}`)
|
||||
.split(`${token}+`).join(`${token}{1,${max}}`)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
const createToken = (name, value, isGlobal) => {
|
||||
// Replace all greedy whitespace to prevent regex dos issues. These regex are
|
||||
// used internally via the safeRe object since all inputs in this library get
|
||||
// normalized first to trim and collapse all extra whitespace. The original
|
||||
// regexes are exported for userland consumption and lower level usage. A
|
||||
// future breaking change could export the safer regex only with a note that
|
||||
// all input should have extra whitespace removed.
|
||||
const safe = value
|
||||
.split('\\s*').join('\\s{0,1}')
|
||||
.split('\\s+').join('\\s')
|
||||
const safe = makeSafeRegex(value)
|
||||
const index = R++
|
||||
debug(name, index, value)
|
||||
t[name] = index
|
||||
@@ -7661,13 +7684,13 @@ const createToken = (name, value, isGlobal) => {
|
||||
// A single `0`, or a non-zero digit followed by zero or more digits.
|
||||
|
||||
createToken('NUMERICIDENTIFIER', '0|[1-9]\\d*')
|
||||
createToken('NUMERICIDENTIFIERLOOSE', '[0-9]+')
|
||||
createToken('NUMERICIDENTIFIERLOOSE', '\\d+')
|
||||
|
||||
// ## Non-numeric Identifier
|
||||
// Zero or more digits, followed by a letter or hyphen, and then zero or
|
||||
// more letters, digits, or hyphens.
|
||||
|
||||
createToken('NONNUMERICIDENTIFIER', '\\d*[a-zA-Z-][a-zA-Z0-9-]*')
|
||||
createToken('NONNUMERICIDENTIFIER', `\\d*[a-zA-Z-]${LETTERDASHNUMBER}*`)
|
||||
|
||||
// ## Main Version
|
||||
// Three dot-separated numeric identifiers.
|
||||
@@ -7702,7 +7725,7 @@ createToken('PRERELEASELOOSE', `(?:-?(${src[t.PRERELEASEIDENTIFIERLOOSE]
|
||||
// ## Build Metadata Identifier
|
||||
// Any combination of digits, letters, or hyphens.
|
||||
|
||||
createToken('BUILDIDENTIFIER', '[0-9A-Za-z-]+')
|
||||
createToken('BUILDIDENTIFIER', `${LETTERDASHNUMBER}+`)
|
||||
|
||||
// ## Build Metadata
|
||||
// Plus sign, followed by one or more period-separated build metadata
|
||||
@@ -10052,15 +10075,16 @@ async function main() {
|
||||
|
||||
if (otpSpec !== 'false') {
|
||||
await installOTP(otpSpec, osVersion, hexMirrors)
|
||||
|
||||
const elixirInstalled = await maybeInstallElixir(
|
||||
elixirSpec,
|
||||
otpSpec,
|
||||
hexMirrors,
|
||||
)
|
||||
|
||||
if (elixirInstalled === true) {
|
||||
const shouldMixRebar = getInput('install-rebar', false)
|
||||
await mix(shouldMixRebar, 'rebar', hexMirrors)
|
||||
|
||||
const shouldMixHex = getInput('install-hex', false)
|
||||
await mix(shouldMixHex, 'hex', hexMirrors)
|
||||
}
|
||||
@@ -10084,7 +10108,6 @@ async function installOTP(otpSpec, osVersion, hexMirrors) {
|
||||
|
||||
async function maybeInstallElixir(elixirSpec, otpSpec, hexMirrors) {
|
||||
let installed = false
|
||||
|
||||
if (elixirSpec) {
|
||||
const elixirVersion = await getElixirVersion(
|
||||
elixirSpec,
|
||||
@@ -10094,6 +10117,7 @@ async function maybeInstallElixir(elixirSpec, otpSpec, hexMirrors) {
|
||||
core.startGroup(`Installing Elixir ${elixirVersion}`)
|
||||
await installer.installElixir(elixirVersion, hexMirrors)
|
||||
core.setOutput('elixir-version', elixirVersion)
|
||||
|
||||
const disableProblemMatchers = getInput('disable_problem_matchers', false)
|
||||
if (disableProblemMatchers === 'false') {
|
||||
const elixirMatchers = __nccwpck_require__.ab + "elixir-matchers.json"
|
||||
@@ -10111,6 +10135,7 @@ async function mixWithMirrors(cmd, args, hexMirrors) {
|
||||
if (hexMirrors.length === 0) {
|
||||
throw new Error('mix failed with every mirror')
|
||||
}
|
||||
|
||||
const [hexMirror, ...hexMirrorsT] = hexMirrors
|
||||
process.env.HEX_MIRROR = hexMirror
|
||||
try {
|
||||
@@ -10120,6 +10145,7 @@ async function mixWithMirrors(cmd, args, hexMirrors) {
|
||||
`mix failed with mirror ${process.env.HEX_MIRROR} with message ${err.message})`,
|
||||
)
|
||||
}
|
||||
|
||||
return mixWithMirrors(cmd, args, hexMirrorsT)
|
||||
}
|
||||
|
||||
@@ -10135,7 +10161,6 @@ async function mix(shouldMix, what, hexMirrors) {
|
||||
|
||||
async function maybeInstallGleam(gleamSpec) {
|
||||
let installed = false
|
||||
|
||||
if (gleamSpec) {
|
||||
const gleamVersion = await getGleamVersion(gleamSpec)
|
||||
core.startGroup(`Installing Gleam ${gleamVersion}`)
|
||||
@@ -10153,7 +10178,6 @@ async function maybeInstallGleam(gleamSpec) {
|
||||
async function maybeInstallRebar3(rebar3Spec) {
|
||||
let installed = false
|
||||
let rebar3Version
|
||||
|
||||
if (rebar3Spec) {
|
||||
if (rebar3Spec === 'nightly') {
|
||||
rebar3Version = 'nightly'
|
||||
@@ -10174,14 +10198,9 @@ async function maybeInstallRebar3(rebar3Spec) {
|
||||
|
||||
async function getOTPVersion(otpSpec0, osVersion, hexMirrors) {
|
||||
const otpVersions = await getOTPVersions(osVersion, hexMirrors)
|
||||
let otpSpec = otpSpec0 // might be a branch (?)
|
||||
const otpVersion = getVersionFromSpec(
|
||||
otpSpec,
|
||||
Array.from(otpVersions.keys()).sort(),
|
||||
)
|
||||
if (isVersion(otpSpec0)) {
|
||||
otpSpec = `OTP-${otpSpec0}` // ... it's a version!
|
||||
}
|
||||
const spec = otpSpec0.replace(/^OTP-/, '')
|
||||
const versions = otpVersions
|
||||
const otpVersion = getVersionFromSpec(spec, versions)
|
||||
if (otpVersion === null) {
|
||||
throw new Error(
|
||||
`Requested Erlang/OTP version (${otpSpec0}) not found in version list ` +
|
||||
@@ -10189,20 +10208,18 @@ async function getOTPVersion(otpSpec0, osVersion, hexMirrors) {
|
||||
)
|
||||
}
|
||||
|
||||
return otpVersions.get(otpVersion) // from the reference, for download
|
||||
return otpVersion // from the reference, for download
|
||||
}
|
||||
|
||||
async function getElixirVersion(exSpec0, otpVersion0, hexMirrors) {
|
||||
const otpVersion = otpVersion0.match(/^([^-]+-)?(.+)$/)[2]
|
||||
const otpVersionMajor = otpVersion.match(/^([^.]+).*$/)[1]
|
||||
const elixirVersions = await getElixirVersions(hexMirrors)
|
||||
const semverVersions = Array.from(elixirVersions.keys()).sort()
|
||||
const exSpec = exSpec0.replace(/-otp-.*$/, '')
|
||||
const elixirVersionFromSpec = getVersionFromSpec(exSpec, semverVersions, true)
|
||||
let elixirVersionForDownload = elixirVersionFromSpec
|
||||
if (isVersion(otpVersionMajor)) {
|
||||
elixirVersionForDownload = `${elixirVersionFromSpec}-otp-${otpVersionMajor}`
|
||||
}
|
||||
const [otpVersionsForElixirMap, elixirVersions] = await getElixirVersions(
|
||||
hexMirrors,
|
||||
)
|
||||
const spec = exSpec0.replace(/-otp-.*$/, '')
|
||||
const versions = elixirVersions
|
||||
const elixirVersionFromSpec = getVersionFromSpec(spec, versions)
|
||||
if (elixirVersionFromSpec === null) {
|
||||
throw new Error(
|
||||
`Requested Elixir version (${exSpec0}) not found in version list ` +
|
||||
@@ -10210,7 +10227,7 @@ async function getElixirVersion(exSpec0, otpVersion0, hexMirrors) {
|
||||
)
|
||||
}
|
||||
|
||||
const elixirVersionComp = elixirVersions.get(elixirVersionFromSpec)
|
||||
const elixirVersionComp = otpVersionsForElixirMap[elixirVersionFromSpec]
|
||||
if (
|
||||
(elixirVersionComp && elixirVersionComp.includes(otpVersionMajor)) ||
|
||||
!isVersion(otpVersionMajor)
|
||||
@@ -10227,13 +10244,20 @@ async function getElixirVersion(exSpec0, otpVersion0, hexMirrors) {
|
||||
)
|
||||
}
|
||||
|
||||
let elixirVersionForDownload = elixirVersionFromSpec
|
||||
|
||||
if (isVersion(otpVersionMajor)) {
|
||||
elixirVersionForDownload = `${elixirVersionFromSpec}-otp-${otpVersionMajor}`
|
||||
}
|
||||
|
||||
return maybePrependWithV(elixirVersionForDownload)
|
||||
}
|
||||
|
||||
async function getGleamVersion(gleamSpec0) {
|
||||
const gleamSpec = gleamSpec0.match(/^v?(.+)$/)
|
||||
const gleamVersions = await getGleamVersions()
|
||||
const gleamVersion = getVersionFromSpec(gleamSpec[1], gleamVersions, true)
|
||||
const spec = gleamSpec0
|
||||
const versions = gleamVersions
|
||||
const gleamVersion = getVersionFromSpec(spec, versions)
|
||||
if (gleamVersion === null) {
|
||||
throw new Error(
|
||||
`Requested Gleam version (${gleamSpec0}) not found in version list ` +
|
||||
@@ -10241,12 +10265,14 @@ async function getGleamVersion(gleamSpec0) {
|
||||
)
|
||||
}
|
||||
|
||||
return maybePrependWithV(gleamVersion, gleamVersion)
|
||||
return maybePrependWithV(gleamVersion)
|
||||
}
|
||||
|
||||
async function getRebar3Version(r3Spec) {
|
||||
const rebar3Versions = await getRebar3Versions()
|
||||
const rebar3Version = getVersionFromSpec(r3Spec, rebar3Versions)
|
||||
const spec = r3Spec
|
||||
const versions = rebar3Versions
|
||||
const rebar3Version = getVersionFromSpec(spec, versions)
|
||||
if (rebar3Version === null) {
|
||||
throw new Error(
|
||||
`Requested rebar3 version (${r3Spec}) not found in version list ` +
|
||||
@@ -10269,8 +10295,9 @@ async function getOTPVersions(osVersion, hexMirrors) {
|
||||
otpVersionsListings = await get(originListing, [1, 2, 3])
|
||||
}
|
||||
|
||||
const otpVersions = new Map()
|
||||
debugLog(`OTP versions listings from ${originListing}`, otpVersionsListings)
|
||||
|
||||
const otpVersions = {}
|
||||
if (process.platform === 'linux') {
|
||||
otpVersionsListings
|
||||
.trim()
|
||||
@@ -10280,8 +10307,9 @@ async function getOTPVersions(osVersion, hexMirrors) {
|
||||
.match(/^([^ ]+)?( .+)/)[1]
|
||||
.match(/^([^-]+-)?(.+)$/)
|
||||
const otpVersion = otpMatch[2]
|
||||
const otpVersionOrig = otpMatch[0]
|
||||
debugLog('OTP line and parsing', [line, otpVersion, otpMatch])
|
||||
otpVersions.set(otpVersion, otpMatch[0]) // we keep the original for later reference
|
||||
otpVersions[otpVersion] = otpVersionOrig // we keep the original for later reference
|
||||
})
|
||||
} else if (process.platform === 'win32') {
|
||||
otpVersionsListings.forEach((otpVersionsListing) => {
|
||||
@@ -10293,15 +10321,12 @@ async function getOTPVersions(osVersion, hexMirrors) {
|
||||
const otpMatch = x.name.match(/^otp_win64_(.*).exe$/)
|
||||
const otpVersion = otpMatch[1]
|
||||
debugLog('OTP line and parsing', [otpMatch, otpVersion])
|
||||
otpVersions.set(otpVersion, otpVersion)
|
||||
otpVersions[otpVersion] = otpVersion
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
debugLog(
|
||||
`OTP versions from ${originListing}`,
|
||||
JSON.stringify([...otpVersions.entries()]),
|
||||
)
|
||||
debugLog(`OTP versions from ${originListing}`, JSON.stringify(otpVersions))
|
||||
|
||||
return otpVersions
|
||||
}
|
||||
@@ -10311,7 +10336,8 @@ async function getElixirVersions(hexMirrors) {
|
||||
'/builds/elixir/builds.txt',
|
||||
hexMirrors,
|
||||
)
|
||||
const otpVersionsForElixirMap = new Map()
|
||||
const otpVersionsForElixirMap = {}
|
||||
const elixirVersions = {}
|
||||
|
||||
elixirVersionsListings
|
||||
.trim()
|
||||
@@ -10319,17 +10345,18 @@ async function getElixirVersions(hexMirrors) {
|
||||
.forEach((line) => {
|
||||
const elixirMatch =
|
||||
line.match(/^v?(.+)-otp-([^ ]+)/) || line.match(/^v?([^ ]+)/)
|
||||
const elixirVersion = maybePrependWithV(elixirMatch[1])
|
||||
const elixirVersion = elixirMatch[1]
|
||||
const otpVersion = elixirMatch[2]
|
||||
const otpVersions = otpVersionsForElixirMap.get(elixirVersion) || []
|
||||
const otpVersions = otpVersionsForElixirMap[elixirVersion] || []
|
||||
if (otpVersion) {
|
||||
// -otp- present (special case)
|
||||
otpVersions.push(otpVersion)
|
||||
}
|
||||
otpVersionsForElixirMap.set(elixirVersion, otpVersions)
|
||||
otpVersionsForElixirMap[elixirVersion] = otpVersions
|
||||
elixirVersions[elixirVersion] = elixirVersion
|
||||
})
|
||||
|
||||
return otpVersionsForElixirMap
|
||||
return [otpVersionsForElixirMap, elixirVersions]
|
||||
}
|
||||
|
||||
async function getGleamVersions() {
|
||||
@@ -10337,13 +10364,17 @@ async function getGleamVersions() {
|
||||
'https://api.github.com/repos/gleam-lang/gleam/releases?per_page=100',
|
||||
[1, 2, 3],
|
||||
)
|
||||
const gleamVersionsListing = []
|
||||
const gleamVersionsListing = {}
|
||||
resultJSONs.forEach((resultJSON) => {
|
||||
jsonParseAsList(resultJSON)
|
||||
.map((x) => x.tag_name)
|
||||
.sort()
|
||||
.forEach((v) => gleamVersionsListing.push(v))
|
||||
.forEach((ver) => {
|
||||
const gleamMatch = ver.match(/^v?([^ ]+)/)
|
||||
const gleamVersion = gleamMatch[1]
|
||||
gleamVersionsListing[gleamVersion] = gleamVersion
|
||||
})
|
||||
})
|
||||
|
||||
return gleamVersionsListing
|
||||
}
|
||||
|
||||
@@ -10352,13 +10383,15 @@ async function getRebar3Versions() {
|
||||
'https://api.github.com/repos/erlang/rebar3/releases?per_page=100',
|
||||
[1, 2, 3],
|
||||
)
|
||||
const rebar3VersionsListing = []
|
||||
const rebar3VersionsListing = {}
|
||||
resultJSONs.forEach((resultJSON) => {
|
||||
jsonParseAsList(resultJSON)
|
||||
.map((x) => x.tag_name)
|
||||
.sort()
|
||||
.forEach((v) => rebar3VersionsListing.push(v))
|
||||
.forEach((ver) => {
|
||||
rebar3VersionsListing[ver] = ver
|
||||
})
|
||||
})
|
||||
|
||||
return rebar3VersionsListing
|
||||
}
|
||||
|
||||
@@ -10366,48 +10399,68 @@ function isStrictVersion() {
|
||||
return getInput('version-type', false) === 'strict'
|
||||
}
|
||||
|
||||
function getVersionFromSpec(spec, versions, maybePrependWithV0) {
|
||||
function getVersionFromSpec(spec0, versions0) {
|
||||
const spec = maybeRemoveVPrefix(spec0)
|
||||
|
||||
const altVersions = {}
|
||||
Object.entries(versions0).forEach(([version, altVersion]) => {
|
||||
let coerced
|
||||
if (isStrictVersion() || isRC(version)) {
|
||||
// If `version-type: strict` or version is RC, we just try to remove a potential initial v
|
||||
coerced = maybeRemoveVPrefix(version)
|
||||
} else {
|
||||
// Otherwise, we place the version into a version bucket
|
||||
coerced = maybeCoerced(version)
|
||||
}
|
||||
const alt = (altVersions[coerced] || []).concat(altVersion)
|
||||
alt.sort(sortVersions)
|
||||
altVersions[coerced] = alt
|
||||
})
|
||||
|
||||
let versions = Object.keys(altVersions)
|
||||
|
||||
const rangeForMax = semver.validRange(spec0) || maybeCoerced(spec)
|
||||
const rangeMax = semver.maxSatisfying(versions, rangeForMax)
|
||||
let version = null
|
||||
|
||||
if (spec.match(/rc/) || isStrictVersion()) {
|
||||
version = spec
|
||||
}
|
||||
if (isStrictVersion() || isRC(spec0)) {
|
||||
if (versions0[spec]) {
|
||||
// If `version-type: strict` or version is RC, we obtain it directly
|
||||
version = versions0[spec]
|
||||
}
|
||||
} else if (rangeMax !== null) {
|
||||
// Otherwise, we compare alt. versions' semver ranges to this version, from highest to lowest
|
||||
const thatVersion = spec
|
||||
const thatVersionAbc = versionAbc(thatVersion)
|
||||
const thatVersionAbcRange = semver.validRange(thatVersionAbc)
|
||||
|
||||
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) => {
|
||||
if (!v.match(/rc/)) {
|
||||
// release candidates are opt-in
|
||||
acc[maybeCoerced(v)] = v
|
||||
versions = altVersions[rangeMax]
|
||||
for (let i = versions.length - 1; i >= 0; i -= 1) {
|
||||
const thisVersion = versions[i]
|
||||
const thisVersionAbc = versionAbc(thisVersion)
|
||||
const thisVersionAbcRange = semver.validRange(thisVersionAbc)
|
||||
|
||||
if (
|
||||
thatVersionAbcRange &&
|
||||
semver.intersects(thatVersionAbcRange, thisVersionAbcRange)
|
||||
) {
|
||||
version = thisVersion
|
||||
break
|
||||
}
|
||||
return acc
|
||||
}, {})
|
||||
const rangeForMax = semver.validRange(spec)
|
||||
if (rangeForMax) {
|
||||
version =
|
||||
versionsMap[semver.maxSatisfying(Object.keys(versionsMap), rangeForMax)]
|
||||
} else {
|
||||
version = versionsMap[maybeCoerced(spec)]
|
||||
}
|
||||
}
|
||||
|
||||
let v = version === null || version === undefined ? null : version
|
||||
if (maybePrependWithV0 && v != null) {
|
||||
v = maybePrependWithV(v)
|
||||
}
|
||||
|
||||
if (!versions.includes(v)) {
|
||||
v = null
|
||||
}
|
||||
|
||||
return v
|
||||
return version || null
|
||||
}
|
||||
|
||||
function maybeCoerced(v) {
|
||||
let ret
|
||||
|
||||
let ret = null
|
||||
try {
|
||||
ret = semver.coerce(v).version
|
||||
if (!isRC(v)) {
|
||||
ret = semver.coerce(v).version
|
||||
} else {
|
||||
ret = maybeRemoveVPrefix(v)
|
||||
}
|
||||
} catch {
|
||||
// some stuff can't be coerced, like 'main'
|
||||
ret = v
|
||||
@@ -10420,11 +10473,11 @@ function sortVersions(left, right) {
|
||||
let ret = 0
|
||||
const newL = verAsComparableStr(left)
|
||||
const newR = verAsComparableStr(right)
|
||||
|
||||
function verAsComparableStr(ver) {
|
||||
const matchGroups = 5
|
||||
const verSpec = /([^.]+)?\.?([^.]+)?\.?([^.]+)?\.?([^.]+)?\.?([^.]+)?/
|
||||
const matchGroups = 6
|
||||
const verSpec = xyzAbcVersion('', '')
|
||||
const matches = ver.match(verSpec).splice(1, matchGroups)
|
||||
|
||||
return matches.reduce((acc, v) => acc + (v || '0').padStart(3, '0'), '')
|
||||
}
|
||||
|
||||
@@ -10437,6 +10490,10 @@ function sortVersions(left, right) {
|
||||
return ret
|
||||
}
|
||||
|
||||
function isRC(ver) {
|
||||
return ver.match(xyzAbcVersion('^', '(?:-rc\\.?\\d+)'))
|
||||
}
|
||||
|
||||
function getRunnerOSVersion() {
|
||||
const ImageOSToContainer = {
|
||||
ubuntu18: 'ubuntu-18.04',
|
||||
@@ -10446,7 +10503,6 @@ function getRunnerOSVersion() {
|
||||
win22: 'windows-2022',
|
||||
}
|
||||
const containerFromEnvImageOS = ImageOSToContainer[process.env.ImageOS]
|
||||
|
||||
if (!containerFromEnvImageOS) {
|
||||
throw new Error(
|
||||
"Tried to map a target OS from env. variable 'ImageOS' (got " +
|
||||
@@ -10467,7 +10523,6 @@ async function get(url0, pageIdxs) {
|
||||
const url = new URL(url0)
|
||||
const headers = {}
|
||||
const GithubToken = getInput('github-token', false)
|
||||
|
||||
if (GithubToken && url.host === 'api.github.com') {
|
||||
headers.authorization = `Bearer ${GithubToken}`
|
||||
}
|
||||
@@ -10480,9 +10535,7 @@ async function get(url0, pageIdxs) {
|
||||
allowRetries: true,
|
||||
maxRetries: 3,
|
||||
})
|
||||
|
||||
const response = await httpClient.get(url, headers)
|
||||
|
||||
if (response.statusCode >= 400 && response.statusCode <= 599) {
|
||||
throw new Error(
|
||||
`Got ${response.statusCode} from ${url}. Exiting with error`,
|
||||
@@ -10495,6 +10548,7 @@ async function get(url0, pageIdxs) {
|
||||
if (pageIdxs[0] === null) {
|
||||
return getPage(null)
|
||||
}
|
||||
|
||||
return Promise.all(pageIdxs.map(getPage))
|
||||
}
|
||||
|
||||
@@ -10502,12 +10556,14 @@ async function getWithMirrors(resourcePath, hexMirrors) {
|
||||
if (hexMirrors.length === 0) {
|
||||
throw new Error(`Could not fetch ${resourcePath} from any hex.pm mirror`)
|
||||
}
|
||||
|
||||
const [hexMirror, ...hexMirrorsT] = hexMirrors
|
||||
try {
|
||||
return await get(`${hexMirror}${resourcePath}`, [null])
|
||||
} catch (err) {
|
||||
core.info(`get failed for URL ${hexMirror}${resourcePath}`)
|
||||
}
|
||||
|
||||
return getWithMirrors(resourcePath, hexMirrorsT)
|
||||
}
|
||||
|
||||
@@ -10515,11 +10571,35 @@ function maybePrependWithV(v) {
|
||||
if (isVersion(v)) {
|
||||
return `v${v.replace('v', '')}`
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
function maybeRemoveVPrefix(ver) {
|
||||
let ret = ver
|
||||
if (isVersion(ver)) {
|
||||
ret = ver.replace('v', '')
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
function xyzAbcVersion(pref, suf) {
|
||||
// This accounts for stuff like 6.0.2.0.1.0, as proposed by Erlang's
|
||||
// https://www.erlang.org/doc/system_principles/versions.html
|
||||
const dd = '\\.?(\\d+)?'
|
||||
return new RegExp(
|
||||
`${pref}v?(\\d+)${dd}${dd}${dd}${dd}${dd}(?:-rc\\.?\\d+)?(?:-otp-\\d+)?${suf}`,
|
||||
)
|
||||
}
|
||||
|
||||
function versionAbc(ver) {
|
||||
// For a version like 6.0.2.0.1.0, return 0.1.0
|
||||
return ver.match(/\d+(?:\.[^.]+)?(?:\.[^.]+)?(?:\.)?(.*)/)[1]
|
||||
}
|
||||
|
||||
function isVersion(v) {
|
||||
return /^v?\d+/.test(v)
|
||||
return v.match(xyzAbcVersion('^', '$')) !== null
|
||||
}
|
||||
|
||||
function getInput(inputName, required, alternativeName, alternatives) {
|
||||
@@ -10537,6 +10617,7 @@ alongside ${alternativeName}=${alternativeValue} \
|
||||
} else if (!input) {
|
||||
input = alternativeValue
|
||||
}
|
||||
|
||||
return input
|
||||
}
|
||||
|
||||
@@ -10550,6 +10631,7 @@ function parseVersionFile(versionFilePath0) {
|
||||
`The specified version file, ${versionFilePath0}, does not exist`,
|
||||
)
|
||||
}
|
||||
|
||||
core.startGroup(`Parsing version file at ${versionFilePath0}`)
|
||||
const appVersions = new Map()
|
||||
const versions = fs.readFileSync(versionFilePath, 'utf8')
|
||||
@@ -10583,6 +10665,7 @@ function jsonParseAsList(maybeJson) {
|
||||
if (!Array.isArray(obj)) {
|
||||
throw new Error('expected a list!')
|
||||
}
|
||||
|
||||
return obj
|
||||
} catch (exc) {
|
||||
throw new Error(
|
||||
|
||||
Generated
+972
-3415
File diff suppressed because it is too large
Load Diff
+7
-17
@@ -13,29 +13,19 @@
|
||||
"yamllint": "yamllint .github/workflows/**.yml && yamllint .*.yml && yamllint *.yml",
|
||||
"build-dist": "npm install && npm run build && npm run format && npm run markdownlint && npm run shellcheck && npm run yamllint && npm run jslint"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "npm run format -- --check && npm run build && git add dist/"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "1.10.0",
|
||||
"@actions/exec": "1.1.1",
|
||||
"@actions/http-client": "2.1.0",
|
||||
"@actions/tool-cache": "^2.0.1",
|
||||
"semver": "7.5.2"
|
||||
"@actions/tool-cache": "2.0.1",
|
||||
"semver": "7.5.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vercel/ncc": "0.34.0",
|
||||
"eslint": "8.25.0",
|
||||
"eslint-config-airbnb": "19.0.4",
|
||||
"eslint-plugin-import": "2.26.0",
|
||||
"eslint-plugin-jsx-a11y": "6.6.1",
|
||||
"eslint-plugin-react": "7.31.10",
|
||||
"husky": "8.0.1",
|
||||
"markdownlint": "0.26.2",
|
||||
"prettier": "2.7.1",
|
||||
"shellcheck": "1.1.0",
|
||||
"@vercel/ncc": "0.36.1",
|
||||
"eslint": "8.43.0",
|
||||
"markdownlint-cli": "0.35.0",
|
||||
"prettier": "2.8.8",
|
||||
"shellcheck": "2.2.0",
|
||||
"yaml-lint": "1.7.0",
|
||||
"yarn": "1.22.19"
|
||||
},
|
||||
|
||||
+143
-83
@@ -35,15 +35,16 @@ async function main() {
|
||||
|
||||
if (otpSpec !== 'false') {
|
||||
await installOTP(otpSpec, osVersion, hexMirrors)
|
||||
|
||||
const elixirInstalled = await maybeInstallElixir(
|
||||
elixirSpec,
|
||||
otpSpec,
|
||||
hexMirrors,
|
||||
)
|
||||
|
||||
if (elixirInstalled === true) {
|
||||
const shouldMixRebar = getInput('install-rebar', false)
|
||||
await mix(shouldMixRebar, 'rebar', hexMirrors)
|
||||
|
||||
const shouldMixHex = getInput('install-hex', false)
|
||||
await mix(shouldMixHex, 'hex', hexMirrors)
|
||||
}
|
||||
@@ -67,7 +68,6 @@ async function installOTP(otpSpec, osVersion, hexMirrors) {
|
||||
|
||||
async function maybeInstallElixir(elixirSpec, otpSpec, hexMirrors) {
|
||||
let installed = false
|
||||
|
||||
if (elixirSpec) {
|
||||
const elixirVersion = await getElixirVersion(
|
||||
elixirSpec,
|
||||
@@ -77,6 +77,7 @@ async function maybeInstallElixir(elixirSpec, otpSpec, hexMirrors) {
|
||||
core.startGroup(`Installing Elixir ${elixirVersion}`)
|
||||
await installer.installElixir(elixirVersion, hexMirrors)
|
||||
core.setOutput('elixir-version', elixirVersion)
|
||||
|
||||
const disableProblemMatchers = getInput('disable_problem_matchers', false)
|
||||
if (disableProblemMatchers === 'false') {
|
||||
const elixirMatchers = path.join(
|
||||
@@ -99,6 +100,7 @@ async function mixWithMirrors(cmd, args, hexMirrors) {
|
||||
if (hexMirrors.length === 0) {
|
||||
throw new Error('mix failed with every mirror')
|
||||
}
|
||||
|
||||
const [hexMirror, ...hexMirrorsT] = hexMirrors
|
||||
process.env.HEX_MIRROR = hexMirror
|
||||
try {
|
||||
@@ -108,6 +110,7 @@ async function mixWithMirrors(cmd, args, hexMirrors) {
|
||||
`mix failed with mirror ${process.env.HEX_MIRROR} with message ${err.message})`,
|
||||
)
|
||||
}
|
||||
|
||||
return mixWithMirrors(cmd, args, hexMirrorsT)
|
||||
}
|
||||
|
||||
@@ -123,7 +126,6 @@ async function mix(shouldMix, what, hexMirrors) {
|
||||
|
||||
async function maybeInstallGleam(gleamSpec) {
|
||||
let installed = false
|
||||
|
||||
if (gleamSpec) {
|
||||
const gleamVersion = await getGleamVersion(gleamSpec)
|
||||
core.startGroup(`Installing Gleam ${gleamVersion}`)
|
||||
@@ -141,7 +143,6 @@ async function maybeInstallGleam(gleamSpec) {
|
||||
async function maybeInstallRebar3(rebar3Spec) {
|
||||
let installed = false
|
||||
let rebar3Version
|
||||
|
||||
if (rebar3Spec) {
|
||||
if (rebar3Spec === 'nightly') {
|
||||
rebar3Version = 'nightly'
|
||||
@@ -162,14 +163,9 @@ async function maybeInstallRebar3(rebar3Spec) {
|
||||
|
||||
async function getOTPVersion(otpSpec0, osVersion, hexMirrors) {
|
||||
const otpVersions = await getOTPVersions(osVersion, hexMirrors)
|
||||
let otpSpec = otpSpec0 // might be a branch (?)
|
||||
const otpVersion = getVersionFromSpec(
|
||||
otpSpec,
|
||||
Array.from(otpVersions.keys()).sort(),
|
||||
)
|
||||
if (isVersion(otpSpec0)) {
|
||||
otpSpec = `OTP-${otpSpec0}` // ... it's a version!
|
||||
}
|
||||
const spec = otpSpec0.replace(/^OTP-/, '')
|
||||
const versions = otpVersions
|
||||
const otpVersion = getVersionFromSpec(spec, versions)
|
||||
if (otpVersion === null) {
|
||||
throw new Error(
|
||||
`Requested Erlang/OTP version (${otpSpec0}) not found in version list ` +
|
||||
@@ -177,20 +173,18 @@ async function getOTPVersion(otpSpec0, osVersion, hexMirrors) {
|
||||
)
|
||||
}
|
||||
|
||||
return otpVersions.get(otpVersion) // from the reference, for download
|
||||
return otpVersion // from the reference, for download
|
||||
}
|
||||
|
||||
async function getElixirVersion(exSpec0, otpVersion0, hexMirrors) {
|
||||
const otpVersion = otpVersion0.match(/^([^-]+-)?(.+)$/)[2]
|
||||
const otpVersionMajor = otpVersion.match(/^([^.]+).*$/)[1]
|
||||
const elixirVersions = await getElixirVersions(hexMirrors)
|
||||
const semverVersions = Array.from(elixirVersions.keys()).sort()
|
||||
const exSpec = exSpec0.replace(/-otp-.*$/, '')
|
||||
const elixirVersionFromSpec = getVersionFromSpec(exSpec, semverVersions, true)
|
||||
let elixirVersionForDownload = elixirVersionFromSpec
|
||||
if (isVersion(otpVersionMajor)) {
|
||||
elixirVersionForDownload = `${elixirVersionFromSpec}-otp-${otpVersionMajor}`
|
||||
}
|
||||
const [otpVersionsForElixirMap, elixirVersions] = await getElixirVersions(
|
||||
hexMirrors,
|
||||
)
|
||||
const spec = exSpec0.replace(/-otp-.*$/, '')
|
||||
const versions = elixirVersions
|
||||
const elixirVersionFromSpec = getVersionFromSpec(spec, versions)
|
||||
if (elixirVersionFromSpec === null) {
|
||||
throw new Error(
|
||||
`Requested Elixir version (${exSpec0}) not found in version list ` +
|
||||
@@ -198,7 +192,7 @@ async function getElixirVersion(exSpec0, otpVersion0, hexMirrors) {
|
||||
)
|
||||
}
|
||||
|
||||
const elixirVersionComp = elixirVersions.get(elixirVersionFromSpec)
|
||||
const elixirVersionComp = otpVersionsForElixirMap[elixirVersionFromSpec]
|
||||
if (
|
||||
(elixirVersionComp && elixirVersionComp.includes(otpVersionMajor)) ||
|
||||
!isVersion(otpVersionMajor)
|
||||
@@ -215,13 +209,20 @@ async function getElixirVersion(exSpec0, otpVersion0, hexMirrors) {
|
||||
)
|
||||
}
|
||||
|
||||
let elixirVersionForDownload = elixirVersionFromSpec
|
||||
|
||||
if (isVersion(otpVersionMajor)) {
|
||||
elixirVersionForDownload = `${elixirVersionFromSpec}-otp-${otpVersionMajor}`
|
||||
}
|
||||
|
||||
return maybePrependWithV(elixirVersionForDownload)
|
||||
}
|
||||
|
||||
async function getGleamVersion(gleamSpec0) {
|
||||
const gleamSpec = gleamSpec0.match(/^v?(.+)$/)
|
||||
const gleamVersions = await getGleamVersions()
|
||||
const gleamVersion = getVersionFromSpec(gleamSpec[1], gleamVersions, true)
|
||||
const spec = gleamSpec0
|
||||
const versions = gleamVersions
|
||||
const gleamVersion = getVersionFromSpec(spec, versions)
|
||||
if (gleamVersion === null) {
|
||||
throw new Error(
|
||||
`Requested Gleam version (${gleamSpec0}) not found in version list ` +
|
||||
@@ -229,12 +230,14 @@ async function getGleamVersion(gleamSpec0) {
|
||||
)
|
||||
}
|
||||
|
||||
return maybePrependWithV(gleamVersion, gleamVersion)
|
||||
return maybePrependWithV(gleamVersion)
|
||||
}
|
||||
|
||||
async function getRebar3Version(r3Spec) {
|
||||
const rebar3Versions = await getRebar3Versions()
|
||||
const rebar3Version = getVersionFromSpec(r3Spec, rebar3Versions)
|
||||
const spec = r3Spec
|
||||
const versions = rebar3Versions
|
||||
const rebar3Version = getVersionFromSpec(spec, versions)
|
||||
if (rebar3Version === null) {
|
||||
throw new Error(
|
||||
`Requested rebar3 version (${r3Spec}) not found in version list ` +
|
||||
@@ -257,8 +260,9 @@ async function getOTPVersions(osVersion, hexMirrors) {
|
||||
otpVersionsListings = await get(originListing, [1, 2, 3])
|
||||
}
|
||||
|
||||
const otpVersions = new Map()
|
||||
debugLog(`OTP versions listings from ${originListing}`, otpVersionsListings)
|
||||
|
||||
const otpVersions = {}
|
||||
if (process.platform === 'linux') {
|
||||
otpVersionsListings
|
||||
.trim()
|
||||
@@ -268,8 +272,9 @@ async function getOTPVersions(osVersion, hexMirrors) {
|
||||
.match(/^([^ ]+)?( .+)/)[1]
|
||||
.match(/^([^-]+-)?(.+)$/)
|
||||
const otpVersion = otpMatch[2]
|
||||
const otpVersionOrig = otpMatch[0]
|
||||
debugLog('OTP line and parsing', [line, otpVersion, otpMatch])
|
||||
otpVersions.set(otpVersion, otpMatch[0]) // we keep the original for later reference
|
||||
otpVersions[otpVersion] = otpVersionOrig // we keep the original for later reference
|
||||
})
|
||||
} else if (process.platform === 'win32') {
|
||||
otpVersionsListings.forEach((otpVersionsListing) => {
|
||||
@@ -281,15 +286,12 @@ async function getOTPVersions(osVersion, hexMirrors) {
|
||||
const otpMatch = x.name.match(/^otp_win64_(.*).exe$/)
|
||||
const otpVersion = otpMatch[1]
|
||||
debugLog('OTP line and parsing', [otpMatch, otpVersion])
|
||||
otpVersions.set(otpVersion, otpVersion)
|
||||
otpVersions[otpVersion] = otpVersion
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
debugLog(
|
||||
`OTP versions from ${originListing}`,
|
||||
JSON.stringify([...otpVersions.entries()]),
|
||||
)
|
||||
debugLog(`OTP versions from ${originListing}`, JSON.stringify(otpVersions))
|
||||
|
||||
return otpVersions
|
||||
}
|
||||
@@ -299,7 +301,8 @@ async function getElixirVersions(hexMirrors) {
|
||||
'/builds/elixir/builds.txt',
|
||||
hexMirrors,
|
||||
)
|
||||
const otpVersionsForElixirMap = new Map()
|
||||
const otpVersionsForElixirMap = {}
|
||||
const elixirVersions = {}
|
||||
|
||||
elixirVersionsListings
|
||||
.trim()
|
||||
@@ -307,17 +310,18 @@ async function getElixirVersions(hexMirrors) {
|
||||
.forEach((line) => {
|
||||
const elixirMatch =
|
||||
line.match(/^v?(.+)-otp-([^ ]+)/) || line.match(/^v?([^ ]+)/)
|
||||
const elixirVersion = maybePrependWithV(elixirMatch[1])
|
||||
const elixirVersion = elixirMatch[1]
|
||||
const otpVersion = elixirMatch[2]
|
||||
const otpVersions = otpVersionsForElixirMap.get(elixirVersion) || []
|
||||
const otpVersions = otpVersionsForElixirMap[elixirVersion] || []
|
||||
if (otpVersion) {
|
||||
// -otp- present (special case)
|
||||
otpVersions.push(otpVersion)
|
||||
}
|
||||
otpVersionsForElixirMap.set(elixirVersion, otpVersions)
|
||||
otpVersionsForElixirMap[elixirVersion] = otpVersions
|
||||
elixirVersions[elixirVersion] = elixirVersion
|
||||
})
|
||||
|
||||
return otpVersionsForElixirMap
|
||||
return [otpVersionsForElixirMap, elixirVersions]
|
||||
}
|
||||
|
||||
async function getGleamVersions() {
|
||||
@@ -325,13 +329,17 @@ async function getGleamVersions() {
|
||||
'https://api.github.com/repos/gleam-lang/gleam/releases?per_page=100',
|
||||
[1, 2, 3],
|
||||
)
|
||||
const gleamVersionsListing = []
|
||||
const gleamVersionsListing = {}
|
||||
resultJSONs.forEach((resultJSON) => {
|
||||
jsonParseAsList(resultJSON)
|
||||
.map((x) => x.tag_name)
|
||||
.sort()
|
||||
.forEach((v) => gleamVersionsListing.push(v))
|
||||
.forEach((ver) => {
|
||||
const gleamMatch = ver.match(/^v?([^ ]+)/)
|
||||
const gleamVersion = gleamMatch[1]
|
||||
gleamVersionsListing[gleamVersion] = gleamVersion
|
||||
})
|
||||
})
|
||||
|
||||
return gleamVersionsListing
|
||||
}
|
||||
|
||||
@@ -340,13 +348,15 @@ async function getRebar3Versions() {
|
||||
'https://api.github.com/repos/erlang/rebar3/releases?per_page=100',
|
||||
[1, 2, 3],
|
||||
)
|
||||
const rebar3VersionsListing = []
|
||||
const rebar3VersionsListing = {}
|
||||
resultJSONs.forEach((resultJSON) => {
|
||||
jsonParseAsList(resultJSON)
|
||||
.map((x) => x.tag_name)
|
||||
.sort()
|
||||
.forEach((v) => rebar3VersionsListing.push(v))
|
||||
.forEach((ver) => {
|
||||
rebar3VersionsListing[ver] = ver
|
||||
})
|
||||
})
|
||||
|
||||
return rebar3VersionsListing
|
||||
}
|
||||
|
||||
@@ -354,48 +364,68 @@ function isStrictVersion() {
|
||||
return getInput('version-type', false) === 'strict'
|
||||
}
|
||||
|
||||
function getVersionFromSpec(spec, versions, maybePrependWithV0) {
|
||||
function getVersionFromSpec(spec0, versions0) {
|
||||
const spec = maybeRemoveVPrefix(spec0)
|
||||
|
||||
const altVersions = {}
|
||||
Object.entries(versions0).forEach(([version, altVersion]) => {
|
||||
let coerced
|
||||
if (isStrictVersion() || isRC(version)) {
|
||||
// If `version-type: strict` or version is RC, we just try to remove a potential initial v
|
||||
coerced = maybeRemoveVPrefix(version)
|
||||
} else {
|
||||
// Otherwise, we place the version into a version bucket
|
||||
coerced = maybeCoerced(version)
|
||||
}
|
||||
const alt = (altVersions[coerced] || []).concat(altVersion)
|
||||
alt.sort(sortVersions)
|
||||
altVersions[coerced] = alt
|
||||
})
|
||||
|
||||
let versions = Object.keys(altVersions)
|
||||
|
||||
const rangeForMax = semver.validRange(spec0) || maybeCoerced(spec)
|
||||
const rangeMax = semver.maxSatisfying(versions, rangeForMax)
|
||||
let version = null
|
||||
|
||||
if (spec.match(/rc/) || isStrictVersion()) {
|
||||
version = spec
|
||||
}
|
||||
if (isStrictVersion() || isRC(spec0)) {
|
||||
if (versions0[spec]) {
|
||||
// If `version-type: strict` or version is RC, we obtain it directly
|
||||
version = versions0[spec]
|
||||
}
|
||||
} else if (rangeMax !== null) {
|
||||
// Otherwise, we compare alt. versions' semver ranges to this version, from highest to lowest
|
||||
const thatVersion = spec
|
||||
const thatVersionAbc = versionAbc(thatVersion)
|
||||
const thatVersionAbcRange = semver.validRange(thatVersionAbc)
|
||||
|
||||
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) => {
|
||||
if (!v.match(/rc/)) {
|
||||
// release candidates are opt-in
|
||||
acc[maybeCoerced(v)] = v
|
||||
versions = altVersions[rangeMax]
|
||||
for (let i = versions.length - 1; i >= 0; i -= 1) {
|
||||
const thisVersion = versions[i]
|
||||
const thisVersionAbc = versionAbc(thisVersion)
|
||||
const thisVersionAbcRange = semver.validRange(thisVersionAbc)
|
||||
|
||||
if (
|
||||
thatVersionAbcRange &&
|
||||
semver.intersects(thatVersionAbcRange, thisVersionAbcRange)
|
||||
) {
|
||||
version = thisVersion
|
||||
break
|
||||
}
|
||||
return acc
|
||||
}, {})
|
||||
const rangeForMax = semver.validRange(spec)
|
||||
if (rangeForMax) {
|
||||
version =
|
||||
versionsMap[semver.maxSatisfying(Object.keys(versionsMap), rangeForMax)]
|
||||
} else {
|
||||
version = versionsMap[maybeCoerced(spec)]
|
||||
}
|
||||
}
|
||||
|
||||
let v = version === null || version === undefined ? null : version
|
||||
if (maybePrependWithV0 && v != null) {
|
||||
v = maybePrependWithV(v)
|
||||
}
|
||||
|
||||
if (!versions.includes(v)) {
|
||||
v = null
|
||||
}
|
||||
|
||||
return v
|
||||
return version || null
|
||||
}
|
||||
|
||||
function maybeCoerced(v) {
|
||||
let ret
|
||||
|
||||
let ret = null
|
||||
try {
|
||||
ret = semver.coerce(v).version
|
||||
if (!isRC(v)) {
|
||||
ret = semver.coerce(v).version
|
||||
} else {
|
||||
ret = maybeRemoveVPrefix(v)
|
||||
}
|
||||
} catch {
|
||||
// some stuff can't be coerced, like 'main'
|
||||
ret = v
|
||||
@@ -408,11 +438,11 @@ function sortVersions(left, right) {
|
||||
let ret = 0
|
||||
const newL = verAsComparableStr(left)
|
||||
const newR = verAsComparableStr(right)
|
||||
|
||||
function verAsComparableStr(ver) {
|
||||
const matchGroups = 5
|
||||
const verSpec = /([^.]+)?\.?([^.]+)?\.?([^.]+)?\.?([^.]+)?\.?([^.]+)?/
|
||||
const matchGroups = 6
|
||||
const verSpec = xyzAbcVersion('', '')
|
||||
const matches = ver.match(verSpec).splice(1, matchGroups)
|
||||
|
||||
return matches.reduce((acc, v) => acc + (v || '0').padStart(3, '0'), '')
|
||||
}
|
||||
|
||||
@@ -425,6 +455,10 @@ function sortVersions(left, right) {
|
||||
return ret
|
||||
}
|
||||
|
||||
function isRC(ver) {
|
||||
return ver.match(xyzAbcVersion('^', '(?:-rc\\.?\\d+)'))
|
||||
}
|
||||
|
||||
function getRunnerOSVersion() {
|
||||
const ImageOSToContainer = {
|
||||
ubuntu18: 'ubuntu-18.04',
|
||||
@@ -434,7 +468,6 @@ function getRunnerOSVersion() {
|
||||
win22: 'windows-2022',
|
||||
}
|
||||
const containerFromEnvImageOS = ImageOSToContainer[process.env.ImageOS]
|
||||
|
||||
if (!containerFromEnvImageOS) {
|
||||
throw new Error(
|
||||
"Tried to map a target OS from env. variable 'ImageOS' (got " +
|
||||
@@ -455,7 +488,6 @@ async function get(url0, pageIdxs) {
|
||||
const url = new URL(url0)
|
||||
const headers = {}
|
||||
const GithubToken = getInput('github-token', false)
|
||||
|
||||
if (GithubToken && url.host === 'api.github.com') {
|
||||
headers.authorization = `Bearer ${GithubToken}`
|
||||
}
|
||||
@@ -468,9 +500,7 @@ async function get(url0, pageIdxs) {
|
||||
allowRetries: true,
|
||||
maxRetries: 3,
|
||||
})
|
||||
|
||||
const response = await httpClient.get(url, headers)
|
||||
|
||||
if (response.statusCode >= 400 && response.statusCode <= 599) {
|
||||
throw new Error(
|
||||
`Got ${response.statusCode} from ${url}. Exiting with error`,
|
||||
@@ -483,6 +513,7 @@ async function get(url0, pageIdxs) {
|
||||
if (pageIdxs[0] === null) {
|
||||
return getPage(null)
|
||||
}
|
||||
|
||||
return Promise.all(pageIdxs.map(getPage))
|
||||
}
|
||||
|
||||
@@ -490,12 +521,14 @@ async function getWithMirrors(resourcePath, hexMirrors) {
|
||||
if (hexMirrors.length === 0) {
|
||||
throw new Error(`Could not fetch ${resourcePath} from any hex.pm mirror`)
|
||||
}
|
||||
|
||||
const [hexMirror, ...hexMirrorsT] = hexMirrors
|
||||
try {
|
||||
return await get(`${hexMirror}${resourcePath}`, [null])
|
||||
} catch (err) {
|
||||
core.info(`get failed for URL ${hexMirror}${resourcePath}`)
|
||||
}
|
||||
|
||||
return getWithMirrors(resourcePath, hexMirrorsT)
|
||||
}
|
||||
|
||||
@@ -503,11 +536,35 @@ function maybePrependWithV(v) {
|
||||
if (isVersion(v)) {
|
||||
return `v${v.replace('v', '')}`
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
function maybeRemoveVPrefix(ver) {
|
||||
let ret = ver
|
||||
if (isVersion(ver)) {
|
||||
ret = ver.replace('v', '')
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
function xyzAbcVersion(pref, suf) {
|
||||
// This accounts for stuff like 6.0.2.0.1.0, as proposed by Erlang's
|
||||
// https://www.erlang.org/doc/system_principles/versions.html
|
||||
const dd = '\\.?(\\d+)?'
|
||||
return new RegExp(
|
||||
`${pref}v?(\\d+)${dd}${dd}${dd}${dd}${dd}(?:-rc\\.?\\d+)?(?:-otp-\\d+)?${suf}`,
|
||||
)
|
||||
}
|
||||
|
||||
function versionAbc(ver) {
|
||||
// For a version like 6.0.2.0.1.0, return 0.1.0
|
||||
return ver.match(/\d+(?:\.[^.]+)?(?:\.[^.]+)?(?:\.)?(.*)/)[1]
|
||||
}
|
||||
|
||||
function isVersion(v) {
|
||||
return /^v?\d+/.test(v)
|
||||
return v.match(xyzAbcVersion('^', '$')) !== null
|
||||
}
|
||||
|
||||
function getInput(inputName, required, alternativeName, alternatives) {
|
||||
@@ -525,6 +582,7 @@ alongside ${alternativeName}=${alternativeValue} \
|
||||
} else if (!input) {
|
||||
input = alternativeValue
|
||||
}
|
||||
|
||||
return input
|
||||
}
|
||||
|
||||
@@ -538,6 +596,7 @@ function parseVersionFile(versionFilePath0) {
|
||||
`The specified version file, ${versionFilePath0}, does not exist`,
|
||||
)
|
||||
}
|
||||
|
||||
core.startGroup(`Parsing version file at ${versionFilePath0}`)
|
||||
const appVersions = new Map()
|
||||
const versions = fs.readFileSync(versionFilePath, 'utf8')
|
||||
@@ -571,6 +630,7 @@ function jsonParseAsList(maybeJson) {
|
||||
if (!Array.isArray(obj)) {
|
||||
throw new Error('expected a list!')
|
||||
}
|
||||
|
||||
return obj
|
||||
} catch (exc) {
|
||||
throw new Error(
|
||||
|
||||
+52
-2
@@ -104,9 +104,18 @@ async function testOTPVersions() {
|
||||
let expected
|
||||
let spec
|
||||
let osVersion
|
||||
let before
|
||||
const hexMirrors = ['https://repo.hex.pm', 'https://cdn.jsdelivr.net/hex']
|
||||
|
||||
if (process.platform === 'linux') {
|
||||
before = simulateInput('version-type', 'strict')
|
||||
spec = '25.3.2.1'
|
||||
osVersion = 'ubuntu-20.04'
|
||||
expected = 'OTP-25.3.2.1'
|
||||
got = await setupBeam.getOTPVersion(spec, osVersion, hexMirrors)
|
||||
assert.deepStrictEqual(got, expected)
|
||||
simulateInput('version-type', before)
|
||||
|
||||
spec = '19.3.x'
|
||||
osVersion = 'ubuntu-16.04'
|
||||
expected = 'OTP-19.3.6.13'
|
||||
@@ -131,6 +140,12 @@ async function testOTPVersions() {
|
||||
got = await setupBeam.getOTPVersion(spec, osVersion, hexMirrors)
|
||||
assert.deepStrictEqual(got, expected)
|
||||
|
||||
spec = '20.3.8.26'
|
||||
osVersion = 'ubuntu-20.04'
|
||||
expected = 'OTP-20.3.8.26'
|
||||
got = await setupBeam.getOTPVersion(spec, osVersion, hexMirrors)
|
||||
assert.deepStrictEqual(got, expected)
|
||||
|
||||
spec = '20.x'
|
||||
osVersion = 'ubuntu-20.04'
|
||||
expected = 'OTP-20.3.8.26'
|
||||
@@ -291,7 +306,7 @@ async function testGetVersionFromSpec() {
|
||||
let expected
|
||||
let spec
|
||||
let before
|
||||
const versions = [
|
||||
const versions0 = [
|
||||
'3.2.30.5',
|
||||
'3.2.3.5',
|
||||
'1',
|
||||
@@ -317,10 +332,20 @@ async function testGetVersionFromSpec() {
|
||||
'22.3.4.9.1',
|
||||
'22.3.4.12.1',
|
||||
'22.3.4.10.1',
|
||||
'22.3.4.2',
|
||||
'main',
|
||||
'v11.11.0-rc.0-otp-23',
|
||||
'22.3.4.2',
|
||||
'22.3.4.2.1.0',
|
||||
'12.1.2.2',
|
||||
'12.1.2.4',
|
||||
'12.1.2.0',
|
||||
'12.1.2.3',
|
||||
'12.1.2.3.0',
|
||||
]
|
||||
const versions = {}
|
||||
versions0.forEach((version) => {
|
||||
versions[version] = version
|
||||
})
|
||||
|
||||
spec = '1'
|
||||
expected = '1.1.0'
|
||||
@@ -417,6 +442,31 @@ async function testGetVersionFromSpec() {
|
||||
assert.deepStrictEqual(got, expected)
|
||||
simulateInput('version-type', before)
|
||||
|
||||
spec = '22.3.4.2'
|
||||
expected = '22.3.4.2.1.0'
|
||||
got = setupBeam.getVersionFromSpec(spec, versions)
|
||||
assert.deepStrictEqual(got, expected)
|
||||
|
||||
spec = '22.3.4.2.1'
|
||||
expected = '22.3.4.2.1.0'
|
||||
got = setupBeam.getVersionFromSpec(spec, versions)
|
||||
assert.deepStrictEqual(got, expected)
|
||||
|
||||
spec = '22.3.4.2.1.0'
|
||||
expected = '22.3.4.2.1.0'
|
||||
got = setupBeam.getVersionFromSpec(spec, versions)
|
||||
assert.deepStrictEqual(got, expected)
|
||||
|
||||
spec = '12.1.2.3'
|
||||
expected = '12.1.2.3.0'
|
||||
got = setupBeam.getVersionFromSpec(spec, versions)
|
||||
assert.deepStrictEqual(got, expected)
|
||||
|
||||
spec = '22.3.4.2.1.0.1'
|
||||
expected = null
|
||||
got = setupBeam.getVersionFromSpec(spec, versions)
|
||||
assert.deepStrictEqual(got, expected)
|
||||
|
||||
before = simulateInput('version-type', 'strict')
|
||||
spec = '22.3.4.3'
|
||||
expected = null
|
||||
|
||||
Reference in New Issue
Block a user