From 0b3405b1977108f9a73594ea04c25f0d29b7a9de Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Fri, 23 Jun 2023 05:08:16 +0100 Subject: [PATCH] Execute npm run build-dist and keep results --- dist/index.js | 533 +++++++++++++++++++++++++++++++------------------- 1 file changed, 337 insertions(+), 196 deletions(-) diff --git a/dist/index.js b/dist/index.js index 56a65a6a..8ab814a0 100644 --- a/dist/index.js +++ b/dist/index.js @@ -5991,6 +5991,7 @@ class Comparator { } } + comp = comp.trim().split(/\s+/).join(' ') debug('comparator', comp, options) this.options = options this.loose = !!options.loose @@ -6053,13 +6054,6 @@ class Comparator { throw new TypeError('a Comparator is required') } - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false, - } - } - if (this.operator === '') { if (this.value === '') { return true @@ -6072,39 +6066,50 @@ class Comparator { return new Range(this.value, options).test(comp.semver) } - const sameDirectionIncreasing = - (this.operator === '>=' || this.operator === '>') && - (comp.operator === '>=' || comp.operator === '>') - const sameDirectionDecreasing = - (this.operator === '<=' || this.operator === '<') && - (comp.operator === '<=' || comp.operator === '<') - const sameSemVer = this.semver.version === comp.semver.version - const differentDirectionsInclusive = - (this.operator === '>=' || this.operator === '<=') && - (comp.operator === '>=' || comp.operator === '<=') - const oppositeDirectionsLessThan = - cmp(this.semver, '<', comp.semver, options) && - (this.operator === '>=' || this.operator === '>') && - (comp.operator === '<=' || comp.operator === '<') - const oppositeDirectionsGreaterThan = - cmp(this.semver, '>', comp.semver, options) && - (this.operator === '<=' || this.operator === '<') && - (comp.operator === '>=' || comp.operator === '>') + options = parseOptions(options) - return ( - sameDirectionIncreasing || - sameDirectionDecreasing || - (sameSemVer && differentDirectionsInclusive) || - oppositeDirectionsLessThan || - oppositeDirectionsGreaterThan - ) + // Special cases where nothing can possibly be lower + if (options.includePrerelease && + (this.value === '<0.0.0-0' || comp.value === '<0.0.0-0')) { + return false + } + if (!options.includePrerelease && + (this.value.startsWith('<0.0.0') || comp.value.startsWith('<0.0.0'))) { + return false + } + + // Same direction increasing (> or >=) + if (this.operator.startsWith('>') && comp.operator.startsWith('>')) { + return true + } + // Same direction decreasing (< or <=) + if (this.operator.startsWith('<') && comp.operator.startsWith('<')) { + return true + } + // same SemVer and both sides are inclusive (<= or >=) + if ( + (this.semver.version === comp.semver.version) && + this.operator.includes('=') && comp.operator.includes('=')) { + return true + } + // opposite directions less than + if (cmp(this.semver, '<', comp.semver, options) && + this.operator.startsWith('>') && comp.operator.startsWith('<')) { + return true + } + // opposite directions greater than + if (cmp(this.semver, '>', comp.semver, options) && + this.operator.startsWith('<') && comp.operator.startsWith('>')) { + return true + } + return false } } module.exports = Comparator const parseOptions = __nccwpck_require__(785) -const { re, t } = __nccwpck_require__(9523) +const { safeRe: re, t } = __nccwpck_require__(9523) const cmp = __nccwpck_require__(5098) const debug = __nccwpck_require__(427) const SemVer = __nccwpck_require__(8088) @@ -6144,19 +6149,26 @@ class Range { this.loose = !!options.loose this.includePrerelease = !!options.includePrerelease - // First, split based on boolean or || + // First reduce all whitespace as much as possible so we do not have to rely + // on potentially slow regexes like \s*. This is then stored and used for + // future error messages as well. this.raw = range - this.set = range + .trim() + .split(/\s+/) + .join(' ') + + // First, split on || + this.set = this.raw .split('||') // map the range to a 2d array of comparators - .map(r => this.parseRange(r.trim())) + .map(r => this.parseRange(r)) // throw out any comparator lists that are empty // this generally means that it was not a valid range, which is allowed // in loose mode, but will still throw if the WHOLE range is invalid. .filter(c => c.length) if (!this.set.length) { - throw new TypeError(`Invalid SemVer Range: ${range}`) + throw new TypeError(`Invalid SemVer Range: ${this.raw}`) } // if we have any that are not the null set, throw out null sets. @@ -6182,9 +6194,7 @@ class Range { format () { this.range = this.set - .map((comps) => { - return comps.join(' ').trim() - }) + .map((comps) => comps.join(' ').trim()) .join('||') .trim() return this.range @@ -6195,12 +6205,12 @@ class Range { } parseRange (range) { - range = range.trim() - // memoize range parsing for performance. // this is a very hot path, and fully deterministic. - const memoOpts = Object.keys(this.options).join(',') - const memoKey = `parseRange:${memoOpts}:${range}` + const memoOpts = + (this.options.includePrerelease && FLAG_INCLUDE_PRERELEASE) | + (this.options.loose && FLAG_LOOSE) + const memoKey = memoOpts + ':' + range const cached = cache.get(memoKey) if (cached) { return cached @@ -6211,18 +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) - - // normalize spaces - range = range.split(/\s+/).join(' ') + debug('caret trim', range) // At this point, the range is completely trimmed and // ready to be split into comparators. @@ -6308,6 +6318,7 @@ class Range { return false } } + module.exports = Range const LRU = __nccwpck_require__(7129) @@ -6318,12 +6329,13 @@ const Comparator = __nccwpck_require__(1532) const debug = __nccwpck_require__(427) const SemVer = __nccwpck_require__(8088) const { - re, + safeRe: re, t, comparatorTrimReplace, tildeTrimReplace, caretTrimReplace, } = __nccwpck_require__(9523) +const { FLAG_INCLUDE_PRERELEASE, FLAG_LOOSE } = __nccwpck_require__(2293) const isNullSet = c => c.value === '<0.0.0-0' const isAny = c => c.value === '' @@ -6371,10 +6383,13 @@ const isX = id => !id || id.toLowerCase() === 'x' || id === '*' // ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0-0 // ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0-0 // ~0.0.1 --> >=0.0.1 <0.1.0-0 -const replaceTildes = (comp, options) => - comp.trim().split(/\s+/).map((c) => { - return replaceTilde(c, options) - }).join(' ') +const replaceTildes = (comp, options) => { + return comp + .trim() + .split(/\s+/) + .map((c) => replaceTilde(c, options)) + .join(' ') +} const replaceTilde = (comp, options) => { const r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE] @@ -6412,10 +6427,13 @@ const replaceTilde = (comp, options) => { // ^1.2.0 --> >=1.2.0 <2.0.0-0 // ^0.0.1 --> >=0.0.1 <0.0.2-0 // ^0.1.0 --> >=0.1.0 <0.2.0-0 -const replaceCarets = (comp, options) => - comp.trim().split(/\s+/).map((c) => { - return replaceCaret(c, options) - }).join(' ') +const replaceCarets = (comp, options) => { + return comp + .trim() + .split(/\s+/) + .map((c) => replaceCaret(c, options)) + .join(' ') +} const replaceCaret = (comp, options) => { debug('caret', comp, options) @@ -6472,9 +6490,10 @@ const replaceCaret = (comp, options) => { const replaceXRanges = (comp, options) => { debug('replaceXRanges', comp, options) - return comp.split(/\s+/).map((c) => { - return replaceXRange(c, options) - }).join(' ') + return comp + .split(/\s+/) + .map((c) => replaceXRange(c, options)) + .join(' ') } const replaceXRange = (comp, options) => { @@ -6557,12 +6576,15 @@ const replaceXRange = (comp, options) => { const replaceStars = (comp, options) => { debug('replaceStars', comp, options) // Looseness is ignored here. star is always as loose as it gets! - return comp.trim().replace(re[t.STAR], '') + return comp + .trim() + .replace(re[t.STAR], '') } const replaceGTE0 = (comp, options) => { debug('replaceGTE0', comp, options) - return comp.trim() + return comp + .trim() .replace(re[options.includePrerelease ? t.GTE0PRE : t.GTE0], '') } @@ -6600,7 +6622,7 @@ const hyphenReplace = incPr => ($0, to = `<=${to}` } - return (`${from} ${to}`).trim() + return `${from} ${to}`.trim() } const testSet = (set, version, options) => { @@ -6647,7 +6669,7 @@ const testSet = (set, version, options) => { const debug = __nccwpck_require__(427) const { MAX_LENGTH, MAX_SAFE_INTEGER } = __nccwpck_require__(2293) -const { re, t } = __nccwpck_require__(9523) +const { safeRe: re, t } = __nccwpck_require__(9523) const parseOptions = __nccwpck_require__(785) const { compareIdentifiers } = __nccwpck_require__(2463) @@ -6663,7 +6685,7 @@ class SemVer { version = version.version } } else if (typeof version !== 'string') { - throw new TypeError(`Invalid Version: ${version}`) + throw new TypeError(`Invalid version. Must be a string. Got type "${typeof version}".`) } if (version.length > MAX_LENGTH) { @@ -6822,36 +6844,36 @@ class SemVer { // preminor will bump the version up to the next minor release, and immediately // down to pre-release. premajor and prepatch work the same way. - inc (release, identifier) { + inc (release, identifier, identifierBase) { switch (release) { case 'premajor': this.prerelease.length = 0 this.patch = 0 this.minor = 0 this.major++ - this.inc('pre', identifier) + this.inc('pre', identifier, identifierBase) break case 'preminor': this.prerelease.length = 0 this.patch = 0 this.minor++ - this.inc('pre', identifier) + this.inc('pre', identifier, identifierBase) break case 'prepatch': // If this is already a prerelease, it will bump to the next version // drop any prereleases that might already exist, since they are not // relevant at this point. this.prerelease.length = 0 - this.inc('patch', identifier) - this.inc('pre', identifier) + this.inc('patch', identifier, identifierBase) + this.inc('pre', identifier, identifierBase) break // If the input is a non-prerelease version, this acts the same as // prepatch. case 'prerelease': if (this.prerelease.length === 0) { - this.inc('patch', identifier) + this.inc('patch', identifier, identifierBase) } - this.inc('pre', identifier) + this.inc('pre', identifier, identifierBase) break case 'major': @@ -6893,9 +6915,15 @@ class SemVer { break // This probably shouldn't be used publicly. // 1.0.0 'pre' would become 1.0.0-0 which is the wrong direction. - case 'pre': + case 'pre': { + const base = Number(identifierBase) ? 1 : 0 + + if (!identifier && identifierBase === false) { + throw new Error('invalid increment argument: identifier is empty') + } + if (this.prerelease.length === 0) { - this.prerelease = [0] + this.prerelease = [base] } else { let i = this.prerelease.length while (--i >= 0) { @@ -6906,27 +6934,36 @@ class SemVer { } if (i === -1) { // didn't increment anything - this.prerelease.push(0) + if (identifier === this.prerelease.join('.') && identifierBase === false) { + throw new Error('invalid increment argument: identifier already exists') + } + this.prerelease.push(base) } } if (identifier) { // 1.2.0-beta.1 bumps to 1.2.0-beta.2, // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 + let prerelease = [identifier, base] + if (identifierBase === false) { + prerelease = [identifier] + } if (compareIdentifiers(this.prerelease[0], identifier) === 0) { if (isNaN(this.prerelease[1])) { - this.prerelease = [identifier, 0] + this.prerelease = prerelease } } else { - this.prerelease = [identifier, 0] + this.prerelease = prerelease } } break - + } default: throw new Error(`invalid increment argument: ${release}`) } - this.format() - this.raw = this.version + this.raw = this.format() + if (this.build.length) { + this.raw += `+${this.build.join('.')}` + } return this } } @@ -7013,7 +7050,7 @@ module.exports = cmp const SemVer = __nccwpck_require__(8088) const parse = __nccwpck_require__(5925) -const { re, t } = __nccwpck_require__(9523) +const { safeRe: re, t } = __nccwpck_require__(9523) const coerce = (version, options) => { if (version instanceof SemVer) { @@ -7107,27 +7144,69 @@ module.exports = compare /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { const parse = __nccwpck_require__(5925) -const eq = __nccwpck_require__(1898) const diff = (version1, version2) => { - if (eq(version1, version2)) { + const v1 = parse(version1, null, true) + const v2 = parse(version2, null, true) + const comparison = v1.compare(v2) + + if (comparison === 0) { return null - } else { - const v1 = parse(version1) - const v2 = parse(version2) - const hasPre = v1.prerelease.length || v2.prerelease.length - const prefix = hasPre ? 'pre' : '' - const defaultResult = hasPre ? 'prerelease' : '' - for (const key in v1) { - if (key === 'major' || key === 'minor' || key === 'patch') { - if (v1[key] !== v2[key]) { - return prefix + key - } - } + } + + const v1Higher = comparison > 0 + const highVersion = v1Higher ? v1 : v2 + const lowVersion = v1Higher ? v2 : v1 + const highHasPre = !!highVersion.prerelease.length + const lowHasPre = !!lowVersion.prerelease.length + + if (lowHasPre && !highHasPre) { + // Going from prerelease -> no prerelease requires some special casing + + // If the low version has only a major, then it will always be a major + // Some examples: + // 1.0.0-1 -> 1.0.0 + // 1.0.0-1 -> 1.1.1 + // 1.0.0-1 -> 2.0.0 + if (!lowVersion.patch && !lowVersion.minor) { + return 'major' } - return defaultResult // may be undefined + + // Otherwise it can be determined by checking the high version + + if (highVersion.patch) { + // anything higher than a patch bump would result in the wrong version + return 'patch' + } + + if (highVersion.minor) { + // anything higher than a minor bump would result in the wrong version + return 'minor' + } + + // bumping major/minor/patch all have same result + return 'major' + } + + // add the `pre` prefix if we are going to a prerelease version + const prefix = highHasPre ? 'pre' : '' + + if (v1.major !== v2.major) { + return prefix + 'major' + } + + if (v1.minor !== v2.minor) { + return prefix + 'minor' } + + if (v1.patch !== v2.patch) { + return prefix + 'patch' + } + + // high and low are preleases + return 'prerelease' } + module.exports = diff @@ -7168,8 +7247,9 @@ module.exports = gte const SemVer = __nccwpck_require__(8088) -const inc = (version, release, options, identifier) => { +const inc = (version, release, options, identifier, identifierBase) => { if (typeof (options) === 'string') { + identifierBase = identifier identifier = options options = undefined } @@ -7178,7 +7258,7 @@ const inc = (version, release, options, identifier) => { return new SemVer( version instanceof SemVer ? version.version : version, options - ).inc(release, identifier).version + ).inc(release, identifier, identifierBase).version } catch (er) { return null } @@ -7241,35 +7321,18 @@ module.exports = neq /***/ 5925: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -const { MAX_LENGTH } = __nccwpck_require__(2293) -const { re, t } = __nccwpck_require__(9523) const SemVer = __nccwpck_require__(8088) - -const parseOptions = __nccwpck_require__(785) -const parse = (version, options) => { - options = parseOptions(options) - +const parse = (version, options, throwErrors = false) => { if (version instanceof SemVer) { return version } - - if (typeof version !== 'string') { - return null - } - - if (version.length > MAX_LENGTH) { - return null - } - - const r = options.loose ? re[t.LOOSE] : re[t.FULL] - if (!r.test(version)) { - return null - } - try { return new SemVer(version, options) } catch (er) { - return null + if (!throwErrors) { + return null + } + throw er } } @@ -7449,6 +7512,7 @@ module.exports = { src: internalRe.src, tokens: internalRe.t, SEMVER_SPEC_VERSION: constants.SEMVER_SPEC_VERSION, + RELEASE_TYPES: constants.RELEASE_TYPES, compareIdentifiers: identifiers.compareIdentifiers, rcompareIdentifiers: identifiers.rcompareIdentifiers, } @@ -7470,11 +7534,29 @@ 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', + 'minor', + 'preminor', + 'patch', + 'prepatch', + 'prerelease', +] + module.exports = { - SEMVER_SPEC_VERSION, MAX_LENGTH, - MAX_SAFE_INTEGER, MAX_SAFE_COMPONENT_LENGTH, + MAX_SAFE_BUILD_LENGTH, + MAX_SAFE_INTEGER, + RELEASE_TYPES, + SEMVER_SPEC_VERSION, + FLAG_INCLUDE_PRERELEASE: 0b001, + FLAG_LOOSE: 0b010, } @@ -7529,16 +7611,20 @@ module.exports = { /***/ 785: /***/ ((module) => { -// parse out just the options we care about so we always get a consistent -// obj with keys in a consistent order. -const opts = ['includePrerelease', 'loose', 'rtl'] -const parseOptions = options => - !options ? {} - : typeof options !== 'object' ? { loose: true } - : opts.filter(k => options[k]).reduce((o, k) => { - o[k] = true - return o - }, {}) +// parse out just the options we care about +const looseOption = Object.freeze({ loose: true }) +const emptyOpts = Object.freeze({ }) +const parseOptions = options => { + if (!options) { + return emptyOpts + } + + if (typeof options !== 'object') { + return looseOption + } + + return options +} module.exports = parseOptions @@ -7547,22 +7633,48 @@ 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 = {} // The actual regexps go on exports.re const re = exports.re = [] +const safeRe = exports.safeRe = [] 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) => { + const safe = makeSafeRegex(value) const index = R++ debug(name, index, value) t[name] = index src[index] = value re[index] = new RegExp(value, isGlobal ? 'g' : undefined) + safeRe[index] = new RegExp(safe, isGlobal ? 'g' : undefined) } // The following Regular Expressions can be used for tokenizing, @@ -7572,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. @@ -7613,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 @@ -7751,7 +7863,7 @@ const Range = __nccwpck_require__(9828) const intersects = (r1, r2, options) => { r1 = new Range(r1, options) r2 = new Range(r2, options) - return r1.intersects(r2) + return r1.intersects(r2, options) } module.exports = intersects @@ -8114,6 +8226,9 @@ const subset = (sub, dom, options = {}) => { return true } +const minimumVersionWithPreRelease = [new Comparator('>=0.0.0-0')] +const minimumVersion = [new Comparator('>=0.0.0')] + const simpleSubset = (sub, dom, options) => { if (sub === dom) { return true @@ -8123,9 +8238,9 @@ const simpleSubset = (sub, dom, options) => { if (dom.length === 1 && dom[0].semver === ANY) { return true } else if (options.includePrerelease) { - sub = [new Comparator('>=0.0.0-0')] + sub = minimumVersionWithPreRelease } else { - sub = [new Comparator('>=0.0.0')] + sub = minimumVersion } } @@ -8133,7 +8248,7 @@ const simpleSubset = (sub, dom, options) => { if (options.includePrerelease) { return true } else { - dom = [new Comparator('>=0.0.0')] + dom = minimumVersion } } @@ -9912,15 +10027,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) } @@ -9944,7 +10060,6 @@ async function installOTP(otpSpec, osVersion, hexMirrors) { async function maybeInstallElixir(elixirSpec, otpSpec, hexMirrors) { let installed = false - if (elixirSpec) { const elixirVersion = await getElixirVersion( elixirSpec, @@ -9954,6 +10069,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" @@ -9971,6 +10087,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 { @@ -9980,6 +10097,7 @@ async function mixWithMirrors(cmd, args, hexMirrors) { `mix failed with mirror ${process.env.HEX_MIRROR} with message ${err.message})`, ) } + return mixWithMirrors(cmd, args, hexMirrorsT) } @@ -9995,7 +10113,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}`) @@ -10013,7 +10130,6 @@ async function maybeInstallGleam(gleamSpec) { async function maybeInstallRebar3(rebar3Spec) { let installed = false let rebar3Version - if (rebar3Spec) { if (rebar3Spec === 'nightly') { rebar3Version = 'nightly' @@ -10035,13 +10151,13 @@ 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(), - ) + const spec = otpSpec + const versions = otpVersions + const otpVersion = getVersionFromSpec(spec, versions) if (isVersion(otpSpec0)) { otpSpec = `OTP-${otpSpec0}` // ... it's a version! } + if (otpVersion === null) { throw new Error( `Requested Erlang/OTP version (${otpSpec0}) not found in version list ` + @@ -10056,13 +10172,16 @@ 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) + const spec = exSpec + const versions = elixirVersions + const elixirVersionFromSpec = getVersionFromSpec(spec, versions) + let elixirVersionForDownload = elixirVersionFromSpec if (isVersion(otpVersionMajor)) { elixirVersionForDownload = `${elixirVersionFromSpec}-otp-${otpVersionMajor}` } + if (elixirVersionFromSpec === null) { throw new Error( `Requested Elixir version (${exSpec0}) not found in version list ` + @@ -10093,7 +10212,9 @@ async function getElixirVersion(exSpec0, otpVersion0, hexMirrors) { async function getGleamVersion(gleamSpec0) { const gleamSpec = gleamSpec0.match(/^v?(.+)$/) const gleamVersions = await getGleamVersions() - const gleamVersion = getVersionFromSpec(gleamSpec[1], gleamVersions, true) + const spec = gleamSpec[1] + const versions = gleamVersions + const gleamVersion = getVersionFromSpec(spec, versions) if (gleamVersion === null) { throw new Error( `Requested Gleam version (${gleamSpec0}) not found in version list ` + @@ -10101,12 +10222,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 ` + @@ -10128,10 +10251,10 @@ async function getOTPVersions(osVersion, hexMirrors) { 'https://api.github.com/repos/erlang/otp/releases?per_page=100' otpVersionsListings = await get(originListing, [1, 2, 3]) } + debugLog(`OTP versions listings from ${originListing}`, otpVersionsListings) const otpVersions = new Map() - if (process.platform === 'linux') { otpVersionsListings .trim() @@ -10156,6 +10279,7 @@ async function getOTPVersions(osVersion, hexMirrors) { }) }) } + return otpVersions } @@ -10172,7 +10296,7 @@ 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) || [] if (otpVersion) { @@ -10190,13 +10314,13 @@ async function getGleamVersions() { 'https://api.github.com/repos/gleam-lang/gleam/releases?per_page=100', [1, 2, 3], ) - const gleamVersionsListing = [] + const gleamVersionsListing = new Map() resultJSONs.forEach((resultJSON) => { jsonParseAsList(resultJSON) .map((x) => x.tag_name) - .sort() - .forEach((v) => gleamVersionsListing.push(v)) + .forEach((ver) => gleamVersionsListing.set(ver, ver)) }) + return gleamVersionsListing } @@ -10205,13 +10329,13 @@ async function getRebar3Versions() { 'https://api.github.com/repos/erlang/rebar3/releases?per_page=100', [1, 2, 3], ) - const rebar3VersionsListing = [] + const rebar3VersionsListing = new Map() resultJSONs.forEach((resultJSON) => { jsonParseAsList(resultJSON) .map((x) => x.tag_name) - .sort() - .forEach((v) => rebar3VersionsListing.push(v)) + .forEach((ver) => rebar3VersionsListing.set(ver, ver)) }) + return rebar3VersionsListing } @@ -10219,48 +10343,49 @@ function isStrictVersion() { return getInput('version-type', false) === 'strict' } -function getVersionFromSpec(spec, versions, maybePrependWithV0) { - let version = null - - if (spec.match(/rc/) || isStrictVersion()) { - version = spec - } - - 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 - } - return acc - }, {}) - const rangeForMax = semver.validRange(spec) - if (rangeForMax) { - version = - versionsMap[semver.maxSatisfying(Object.keys(versionsMap), rangeForMax)] +function getVersionFromSpec(spec0, versions0) { + const versions = Array.from(versions0.keys()) + versions.forEach((version, index, array) => { + let manipulatedVersion + if (isStrictVersion() || isRC(version)) { + manipulatedVersion = maybeRemoveVPrefix(version) } else { - version = versionsMap[maybeCoerced(spec)] + manipulatedVersion = maybeCoerced(version) } - } + array.splice(index, 1, manipulatedVersion) + }) + versions.sort(sortVersions) - let v = version === null || version === undefined ? null : version - if (maybePrependWithV0 && v != null) { - v = maybePrependWithV(v) - } + let version = null + const isStrictVersionOrRC = isStrictVersion() || isRC(spec0) + if (isStrictVersionOrRC) { + const spec = maybeRemoveVPrefix(spec0) + if (versions.includes(spec)) { + version = spec + } + } else { + const rangeForMax = semver.validRange(spec0) - if (!versions.includes(v)) { - v = null + if (rangeForMax) { + version = semver.maxSatisfying(versions, rangeForMax) + } else if (semver.validRange(version) === null) { + throw new Error( + `you have to set version-type=strict when using a semver-invalid version (got ${spec0})`, + ) + } } - return v + return version || null } function maybeCoerced(v) { let ret - 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 'master' ret = v @@ -10273,11 +10398,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 matches = ver.match(verSpec).splice(1, matchGroups) + return matches.reduce((acc, v) => acc + (v || '0').padStart(3, '0'), '') } @@ -10290,6 +10415,10 @@ function sortVersions(left, right) { return ret } +function isRC(ver) { + return ver.match(/rc/) !== null +} + function getRunnerOSVersion() { const ImageOSToContainer = { ubuntu18: 'ubuntu-18.04', @@ -10299,7 +10428,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 " + @@ -10320,7 +10448,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}` } @@ -10333,9 +10460,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`, @@ -10348,6 +10473,7 @@ async function get(url0, pageIdxs) { if (pageIdxs[0] === null) { return getPage(null) } + return Promise.all(pageIdxs.map(getPage)) } @@ -10355,12 +10481,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) } @@ -10368,9 +10496,19 @@ 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 isVersion(v) { return /^v?\d+/.test(v) } @@ -10390,6 +10528,7 @@ alongside ${alternativeName}=${alternativeValue} \ } else if (!input) { input = alternativeValue } + return input } @@ -10403,6 +10542,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') @@ -10436,6 +10576,7 @@ function jsonParseAsList(maybeJson) { if (!Array.isArray(obj)) { throw new Error('expected a list!') } + return obj } catch (exc) { throw new Error(