From 4ed3595ecaeb3cd6aba6c2916b92663b62b57013 Mon Sep 17 00:00:00 2001 From: Benjamin Schultzer Date: Wed, 12 Jun 2024 18:09:22 -0400 Subject: [PATCH] Taking it to the next level --- README.md | 2 + dist/index.js | 904 ++++--------------------------- package-lock.json | 27 +- package.json | 2 +- src/setup-beam.js | 29 +- test/otp/ubuntu-24.04/builds.txt | 71 +++ test/setup-beam.test.js | 68 ++- 7 files changed, 253 insertions(+), 850 deletions(-) create mode 100644 test/otp/ubuntu-24.04/builds.txt diff --git a/README.md b/README.md index 40b4aad2..79a80f44 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,8 @@ For pre-release versions, such as `v1.11.0-rc.0`, use the full version specifier (`v1.11.0-rc.0`) and set option `version-type` to `strict`. Pre-release versions are opt-in, so `1.11.x` will not match a pre-release. +Use `latest` for the latest version; the latest version is calculated based on all the retrieved versions. Please take a look at the test cases for examples. + ### Compatibility between Operating System and Erlang/OTP This list presents the known working version combos between the target operating system diff --git a/dist/index.js b/dist/index.js index 272eb74a..4bb06199 100644 --- a/dist/index.js +++ b/dist/index.js @@ -5990,8 +5990,8 @@ class Range { module.exports = Range -const LRU = __nccwpck_require__(1196) -const cache = new LRU({ max: 1000 }) +const LRU = __nccwpck_require__(5339) +const cache = new LRU() const parseOptions = __nccwpck_require__(785) const Comparator = __nccwpck_require__(1532) @@ -6262,9 +6262,10 @@ const replaceGTE0 = (comp, options) => { // 1.2 - 3.4.5 => >=1.2.0 <=3.4.5 // 1.2.3 - 3.4 => >=1.2.0 <3.5.0-0 Any 3.4.x will do // 1.2 - 3.4 => >=1.2.0 <3.5.0-0 +// TODO build? const hyphenReplace = incPr => ($0, from, fM, fm, fp, fpr, fb, - to, tM, tm, tp, tpr, tb) => { + to, tM, tm, tp, tpr) => { if (isX(fM)) { from = '' } else if (isX(fm)) { @@ -6496,7 +6497,7 @@ class SemVer { do { const a = this.build[i] const b = other.build[i] - debug('prerelease compare', i, a, b) + debug('build compare', i, a, b) if (a === undefined && b === undefined) { return 0 } else if (b === undefined) { @@ -6738,35 +6739,43 @@ const coerce = (version, options) => { let match = null if (!options.rtl) { - match = version.match(re[t.COERCE]) + match = version.match(options.includePrerelease ? re[t.COERCEFULL] : re[t.COERCE]) } else { // Find the right-most coercible string that does not share // a terminus with a more left-ward coercible string. // Eg, '1.2.3.4' wants to coerce '2.3.4', not '3.4' or '4' + // With includePrerelease option set, '1.2.3.4-rc' wants to coerce '2.3.4-rc', not '2.3.4' // // Walk through the string checking with a /g regexp // Manually set the index so as to pick up overlapping matches. // Stop when we get a match that ends at the string end, since no // coercible string can be more right-ward without the same terminus. + const coerceRtlRegex = options.includePrerelease ? re[t.COERCERTLFULL] : re[t.COERCERTL] let next - while ((next = re[t.COERCERTL].exec(version)) && + while ((next = coerceRtlRegex.exec(version)) && (!match || match.index + match[0].length !== version.length) ) { if (!match || next.index + next[0].length !== match.index + match[0].length) { match = next } - re[t.COERCERTL].lastIndex = next.index + next[1].length + next[2].length + coerceRtlRegex.lastIndex = next.index + next[1].length + next[2].length } // leave it in a clean state - re[t.COERCERTL].lastIndex = -1 + coerceRtlRegex.lastIndex = -1 } if (match === null) { return null } - return parse(`${match[2]}.${match[3] || '0'}.${match[4] || '0'}`, options) + const major = match[2] + const minor = match[3] || '0' + const patch = match[4] || '0' + const prerelease = options.includePrerelease && match[5] ? `-${match[5]}` : '' + const build = options.includePrerelease && match[6] ? `+${match[6]}` : '' + + return parse(`${major}.${minor}.${patch}${prerelease}${build}`, options) } module.exports = coerce @@ -7275,6 +7284,53 @@ module.exports = { } +/***/ }), + +/***/ 5339: +/***/ ((module) => { + +class LRUCache { + constructor () { + this.max = 1000 + this.map = new Map() + } + + get (key) { + const value = this.map.get(key) + if (value === undefined) { + return undefined + } else { + // Remove the key from the map and add it to the end + this.map.delete(key) + this.map.set(key, value) + return value + } + } + + delete (key) { + return this.map.delete(key) + } + + set (key, value) { + const deleted = this.delete(key) + + if (!deleted && value !== undefined) { + // If cache is full, delete the least recently used item + if (this.map.size >= this.max) { + const firstKey = this.map.keys().next().value + this.delete(firstKey) + } + + this.map.set(key, value) + } + + return this + } +} + +module.exports = LRUCache + + /***/ }), /***/ 785: @@ -7458,12 +7514,17 @@ createToken('XRANGELOOSE', `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAINLOOSE]}$`) // Coercion. // Extract anything that could conceivably be a part of a valid semver -createToken('COERCE', `${'(^|[^\\d])' + +createToken('COERCEPLAIN', `${'(^|[^\\d])' + '(\\d{1,'}${MAX_SAFE_COMPONENT_LENGTH}})` + `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?` + - `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?` + + `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?`) +createToken('COERCE', `${src[t.COERCEPLAIN]}(?:$|[^\\d])`) +createToken('COERCEFULL', src[t.COERCEPLAIN] + + `(?:${src[t.PRERELEASE]})?` + + `(?:${src[t.BUILD]})?` + `(?:$|[^\\d])`) createToken('COERCERTL', src[t.COERCE], true) +createToken('COERCERTLFULL', src[t.COERCEFULL], true) // Tilde ranges. // Meaning is "reasonably at or greater than" @@ -7516,348 +7577,6 @@ createToken('GTE0', '^\\s*>=\\s*0\\.0\\.0\\s*$') createToken('GTE0PRE', '^\\s*>=\\s*0\\.0\\.0-0\\s*$') -/***/ }), - -/***/ 1196: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { - -"use strict"; - - -// A linked list to keep track of recently-used-ness -const Yallist = __nccwpck_require__(665) - -const MAX = Symbol('max') -const LENGTH = Symbol('length') -const LENGTH_CALCULATOR = Symbol('lengthCalculator') -const ALLOW_STALE = Symbol('allowStale') -const MAX_AGE = Symbol('maxAge') -const DISPOSE = Symbol('dispose') -const NO_DISPOSE_ON_SET = Symbol('noDisposeOnSet') -const LRU_LIST = Symbol('lruList') -const CACHE = Symbol('cache') -const UPDATE_AGE_ON_GET = Symbol('updateAgeOnGet') - -const naiveLength = () => 1 - -// lruList is a yallist where the head is the youngest -// item, and the tail is the oldest. the list contains the Hit -// objects as the entries. -// Each Hit object has a reference to its Yallist.Node. This -// never changes. -// -// cache is a Map (or PseudoMap) that matches the keys to -// the Yallist.Node object. -class LRUCache { - constructor (options) { - if (typeof options === 'number') - options = { max: options } - - if (!options) - options = {} - - if (options.max && (typeof options.max !== 'number' || options.max < 0)) - throw new TypeError('max must be a non-negative number') - // Kind of weird to have a default max of Infinity, but oh well. - const max = this[MAX] = options.max || Infinity - - const lc = options.length || naiveLength - this[LENGTH_CALCULATOR] = (typeof lc !== 'function') ? naiveLength : lc - this[ALLOW_STALE] = options.stale || false - if (options.maxAge && typeof options.maxAge !== 'number') - throw new TypeError('maxAge must be a number') - this[MAX_AGE] = options.maxAge || 0 - this[DISPOSE] = options.dispose - this[NO_DISPOSE_ON_SET] = options.noDisposeOnSet || false - this[UPDATE_AGE_ON_GET] = options.updateAgeOnGet || false - this.reset() - } - - // resize the cache when the max changes. - set max (mL) { - if (typeof mL !== 'number' || mL < 0) - throw new TypeError('max must be a non-negative number') - - this[MAX] = mL || Infinity - trim(this) - } - get max () { - return this[MAX] - } - - set allowStale (allowStale) { - this[ALLOW_STALE] = !!allowStale - } - get allowStale () { - return this[ALLOW_STALE] - } - - set maxAge (mA) { - if (typeof mA !== 'number') - throw new TypeError('maxAge must be a non-negative number') - - this[MAX_AGE] = mA - trim(this) - } - get maxAge () { - return this[MAX_AGE] - } - - // resize the cache when the lengthCalculator changes. - set lengthCalculator (lC) { - if (typeof lC !== 'function') - lC = naiveLength - - if (lC !== this[LENGTH_CALCULATOR]) { - this[LENGTH_CALCULATOR] = lC - this[LENGTH] = 0 - this[LRU_LIST].forEach(hit => { - hit.length = this[LENGTH_CALCULATOR](hit.value, hit.key) - this[LENGTH] += hit.length - }) - } - trim(this) - } - get lengthCalculator () { return this[LENGTH_CALCULATOR] } - - get length () { return this[LENGTH] } - get itemCount () { return this[LRU_LIST].length } - - rforEach (fn, thisp) { - thisp = thisp || this - for (let walker = this[LRU_LIST].tail; walker !== null;) { - const prev = walker.prev - forEachStep(this, fn, walker, thisp) - walker = prev - } - } - - forEach (fn, thisp) { - thisp = thisp || this - for (let walker = this[LRU_LIST].head; walker !== null;) { - const next = walker.next - forEachStep(this, fn, walker, thisp) - walker = next - } - } - - keys () { - return this[LRU_LIST].toArray().map(k => k.key) - } - - values () { - return this[LRU_LIST].toArray().map(k => k.value) - } - - reset () { - if (this[DISPOSE] && - this[LRU_LIST] && - this[LRU_LIST].length) { - this[LRU_LIST].forEach(hit => this[DISPOSE](hit.key, hit.value)) - } - - this[CACHE] = new Map() // hash of items by key - this[LRU_LIST] = new Yallist() // list of items in order of use recency - this[LENGTH] = 0 // length of items in the list - } - - dump () { - return this[LRU_LIST].map(hit => - isStale(this, hit) ? false : { - k: hit.key, - v: hit.value, - e: hit.now + (hit.maxAge || 0) - }).toArray().filter(h => h) - } - - dumpLru () { - return this[LRU_LIST] - } - - set (key, value, maxAge) { - maxAge = maxAge || this[MAX_AGE] - - if (maxAge && typeof maxAge !== 'number') - throw new TypeError('maxAge must be a number') - - const now = maxAge ? Date.now() : 0 - const len = this[LENGTH_CALCULATOR](value, key) - - if (this[CACHE].has(key)) { - if (len > this[MAX]) { - del(this, this[CACHE].get(key)) - return false - } - - const node = this[CACHE].get(key) - const item = node.value - - // dispose of the old one before overwriting - // split out into 2 ifs for better coverage tracking - if (this[DISPOSE]) { - if (!this[NO_DISPOSE_ON_SET]) - this[DISPOSE](key, item.value) - } - - item.now = now - item.maxAge = maxAge - item.value = value - this[LENGTH] += len - item.length - item.length = len - this.get(key) - trim(this) - return true - } - - const hit = new Entry(key, value, len, now, maxAge) - - // oversized objects fall out of cache automatically. - if (hit.length > this[MAX]) { - if (this[DISPOSE]) - this[DISPOSE](key, value) - - return false - } - - this[LENGTH] += hit.length - this[LRU_LIST].unshift(hit) - this[CACHE].set(key, this[LRU_LIST].head) - trim(this) - return true - } - - has (key) { - if (!this[CACHE].has(key)) return false - const hit = this[CACHE].get(key).value - return !isStale(this, hit) - } - - get (key) { - return get(this, key, true) - } - - peek (key) { - return get(this, key, false) - } - - pop () { - const node = this[LRU_LIST].tail - if (!node) - return null - - del(this, node) - return node.value - } - - del (key) { - del(this, this[CACHE].get(key)) - } - - load (arr) { - // reset the cache - this.reset() - - const now = Date.now() - // A previous serialized cache has the most recent items first - for (let l = arr.length - 1; l >= 0; l--) { - const hit = arr[l] - const expiresAt = hit.e || 0 - if (expiresAt === 0) - // the item was created without expiration in a non aged cache - this.set(hit.k, hit.v) - else { - const maxAge = expiresAt - now - // dont add already expired items - if (maxAge > 0) { - this.set(hit.k, hit.v, maxAge) - } - } - } - } - - prune () { - this[CACHE].forEach((value, key) => get(this, key, false)) - } -} - -const get = (self, key, doUse) => { - const node = self[CACHE].get(key) - if (node) { - const hit = node.value - if (isStale(self, hit)) { - del(self, node) - if (!self[ALLOW_STALE]) - return undefined - } else { - if (doUse) { - if (self[UPDATE_AGE_ON_GET]) - node.value.now = Date.now() - self[LRU_LIST].unshiftNode(node) - } - } - return hit.value - } -} - -const isStale = (self, hit) => { - if (!hit || (!hit.maxAge && !self[MAX_AGE])) - return false - - const diff = Date.now() - hit.now - return hit.maxAge ? diff > hit.maxAge - : self[MAX_AGE] && (diff > self[MAX_AGE]) -} - -const trim = self => { - if (self[LENGTH] > self[MAX]) { - for (let walker = self[LRU_LIST].tail; - self[LENGTH] > self[MAX] && walker !== null;) { - // We know that we're about to delete this one, and also - // what the next least recently used key will be, so just - // go ahead and set it now. - const prev = walker.prev - del(self, walker) - walker = prev - } - } -} - -const del = (self, node) => { - if (node) { - const hit = node.value - if (self[DISPOSE]) - self[DISPOSE](hit.key, hit.value) - - self[LENGTH] -= hit.length - self[CACHE].delete(hit.key) - self[LRU_LIST].removeNode(node) - } -} - -class Entry { - constructor (key, value, length, now, maxAge) { - this.key = key - this.value = value - this.length = length - this.now = now - this.maxAge = maxAge || 0 - } -} - -const forEachStep = (self, fn, node, thisp) => { - let hit = node.value - if (isStale(self, hit)) { - del(self, node) - if (!self[ALLOW_STALE]) - hit = undefined - } - if (hit) - fn.call(thisp, hit.value, hit.key, self) -} - -module.exports = LRUCache - - /***/ }), /***/ 9380: @@ -9379,456 +9098,6 @@ function version(uuid) { var _default = version; exports["default"] = _default; -/***/ }), - -/***/ 4091: -/***/ ((module) => { - -"use strict"; - -module.exports = function (Yallist) { - Yallist.prototype[Symbol.iterator] = function* () { - for (let walker = this.head; walker; walker = walker.next) { - yield walker.value - } - } -} - - -/***/ }), - -/***/ 665: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { - -"use strict"; - -module.exports = Yallist - -Yallist.Node = Node -Yallist.create = Yallist - -function Yallist (list) { - var self = this - if (!(self instanceof Yallist)) { - self = new Yallist() - } - - self.tail = null - self.head = null - self.length = 0 - - if (list && typeof list.forEach === 'function') { - list.forEach(function (item) { - self.push(item) - }) - } else if (arguments.length > 0) { - for (var i = 0, l = arguments.length; i < l; i++) { - self.push(arguments[i]) - } - } - - return self -} - -Yallist.prototype.removeNode = function (node) { - if (node.list !== this) { - throw new Error('removing node which does not belong to this list') - } - - var next = node.next - var prev = node.prev - - if (next) { - next.prev = prev - } - - if (prev) { - prev.next = next - } - - if (node === this.head) { - this.head = next - } - if (node === this.tail) { - this.tail = prev - } - - node.list.length-- - node.next = null - node.prev = null - node.list = null - - return next -} - -Yallist.prototype.unshiftNode = function (node) { - if (node === this.head) { - return - } - - if (node.list) { - node.list.removeNode(node) - } - - var head = this.head - node.list = this - node.next = head - if (head) { - head.prev = node - } - - this.head = node - if (!this.tail) { - this.tail = node - } - this.length++ -} - -Yallist.prototype.pushNode = function (node) { - if (node === this.tail) { - return - } - - if (node.list) { - node.list.removeNode(node) - } - - var tail = this.tail - node.list = this - node.prev = tail - if (tail) { - tail.next = node - } - - this.tail = node - if (!this.head) { - this.head = node - } - this.length++ -} - -Yallist.prototype.push = function () { - for (var i = 0, l = arguments.length; i < l; i++) { - push(this, arguments[i]) - } - return this.length -} - -Yallist.prototype.unshift = function () { - for (var i = 0, l = arguments.length; i < l; i++) { - unshift(this, arguments[i]) - } - return this.length -} - -Yallist.prototype.pop = function () { - if (!this.tail) { - return undefined - } - - var res = this.tail.value - this.tail = this.tail.prev - if (this.tail) { - this.tail.next = null - } else { - this.head = null - } - this.length-- - return res -} - -Yallist.prototype.shift = function () { - if (!this.head) { - return undefined - } - - var res = this.head.value - this.head = this.head.next - if (this.head) { - this.head.prev = null - } else { - this.tail = null - } - this.length-- - return res -} - -Yallist.prototype.forEach = function (fn, thisp) { - thisp = thisp || this - for (var walker = this.head, i = 0; walker !== null; i++) { - fn.call(thisp, walker.value, i, this) - walker = walker.next - } -} - -Yallist.prototype.forEachReverse = function (fn, thisp) { - thisp = thisp || this - for (var walker = this.tail, i = this.length - 1; walker !== null; i--) { - fn.call(thisp, walker.value, i, this) - walker = walker.prev - } -} - -Yallist.prototype.get = function (n) { - for (var i = 0, walker = this.head; walker !== null && i < n; i++) { - // abort out of the list early if we hit a cycle - walker = walker.next - } - if (i === n && walker !== null) { - return walker.value - } -} - -Yallist.prototype.getReverse = function (n) { - for (var i = 0, walker = this.tail; walker !== null && i < n; i++) { - // abort out of the list early if we hit a cycle - walker = walker.prev - } - if (i === n && walker !== null) { - return walker.value - } -} - -Yallist.prototype.map = function (fn, thisp) { - thisp = thisp || this - var res = new Yallist() - for (var walker = this.head; walker !== null;) { - res.push(fn.call(thisp, walker.value, this)) - walker = walker.next - } - return res -} - -Yallist.prototype.mapReverse = function (fn, thisp) { - thisp = thisp || this - var res = new Yallist() - for (var walker = this.tail; walker !== null;) { - res.push(fn.call(thisp, walker.value, this)) - walker = walker.prev - } - return res -} - -Yallist.prototype.reduce = function (fn, initial) { - var acc - var walker = this.head - if (arguments.length > 1) { - acc = initial - } else if (this.head) { - walker = this.head.next - acc = this.head.value - } else { - throw new TypeError('Reduce of empty list with no initial value') - } - - for (var i = 0; walker !== null; i++) { - acc = fn(acc, walker.value, i) - walker = walker.next - } - - return acc -} - -Yallist.prototype.reduceReverse = function (fn, initial) { - var acc - var walker = this.tail - if (arguments.length > 1) { - acc = initial - } else if (this.tail) { - walker = this.tail.prev - acc = this.tail.value - } else { - throw new TypeError('Reduce of empty list with no initial value') - } - - for (var i = this.length - 1; walker !== null; i--) { - acc = fn(acc, walker.value, i) - walker = walker.prev - } - - return acc -} - -Yallist.prototype.toArray = function () { - var arr = new Array(this.length) - for (var i = 0, walker = this.head; walker !== null; i++) { - arr[i] = walker.value - walker = walker.next - } - return arr -} - -Yallist.prototype.toArrayReverse = function () { - var arr = new Array(this.length) - for (var i = 0, walker = this.tail; walker !== null; i++) { - arr[i] = walker.value - walker = walker.prev - } - return arr -} - -Yallist.prototype.slice = function (from, to) { - to = to || this.length - if (to < 0) { - to += this.length - } - from = from || 0 - if (from < 0) { - from += this.length - } - var ret = new Yallist() - if (to < from || to < 0) { - return ret - } - if (from < 0) { - from = 0 - } - if (to > this.length) { - to = this.length - } - for (var i = 0, walker = this.head; walker !== null && i < from; i++) { - walker = walker.next - } - for (; walker !== null && i < to; i++, walker = walker.next) { - ret.push(walker.value) - } - return ret -} - -Yallist.prototype.sliceReverse = function (from, to) { - to = to || this.length - if (to < 0) { - to += this.length - } - from = from || 0 - if (from < 0) { - from += this.length - } - var ret = new Yallist() - if (to < from || to < 0) { - return ret - } - if (from < 0) { - from = 0 - } - if (to > this.length) { - to = this.length - } - for (var i = this.length, walker = this.tail; walker !== null && i > to; i--) { - walker = walker.prev - } - for (; walker !== null && i > from; i--, walker = walker.prev) { - ret.push(walker.value) - } - return ret -} - -Yallist.prototype.splice = function (start, deleteCount, ...nodes) { - if (start > this.length) { - start = this.length - 1 - } - if (start < 0) { - start = this.length + start; - } - - for (var i = 0, walker = this.head; walker !== null && i < start; i++) { - walker = walker.next - } - - var ret = [] - for (var i = 0; walker && i < deleteCount; i++) { - ret.push(walker.value) - walker = this.removeNode(walker) - } - if (walker === null) { - walker = this.tail - } - - if (walker !== this.head && walker !== this.tail) { - walker = walker.prev - } - - for (var i = 0; i < nodes.length; i++) { - walker = insert(this, walker, nodes[i]) - } - return ret; -} - -Yallist.prototype.reverse = function () { - var head = this.head - var tail = this.tail - for (var walker = head; walker !== null; walker = walker.prev) { - var p = walker.prev - walker.prev = walker.next - walker.next = p - } - this.head = tail - this.tail = head - return this -} - -function insert (self, node, value) { - var inserted = node === self.head ? - new Node(value, null, node, self) : - new Node(value, node, node.next, self) - - if (inserted.next === null) { - self.tail = inserted - } - if (inserted.prev === null) { - self.head = inserted - } - - self.length++ - - return inserted -} - -function push (self, item) { - self.tail = new Node(item, self.tail, null, self) - if (!self.head) { - self.head = self.tail - } - self.length++ -} - -function unshift (self, item) { - self.head = new Node(item, null, self.head, self) - if (!self.tail) { - self.tail = self.head - } - self.length++ -} - -function Node (value, prev, next, list) { - if (!(this instanceof Node)) { - return new Node(value, prev, next, list) - } - - this.list = list - this.value = value - - if (prev) { - prev.next = this - this.prev = prev - } else { - this.prev = null - } - - if (next) { - next.prev = this - this.next = next - } else { - this.next = null - } -} - -try { - // add if support for Symbol.iterator is present - __nccwpck_require__(4091)(Yallist) -} catch (er) {} - - /***/ }), /***/ 7037: @@ -10209,15 +9478,34 @@ function isStrictVersion() { return getInput('version-type', false) === 'strict' } +function gt(left, right) { + return semver.gt(parseVersion(left), parseVersion(right)) +} + +function validVersion(v) { + return ( + v.match(/main|master|nightly|latest/g) == null && + !v.startsWith('a') && + !v.startsWith('b') + ) +} + +function parseVersion(v) { + v = v.includes('rc') ? v : v.split('.') + if (v instanceof Array) { + v = `${[v.shift(), v.shift(), v.shift()].join('.')}+${v.join('.')}` + } + return semver.coerce(v, { includePrerelease: true, loose: true }) +} + function getVersionFromSpec(spec0, versions0) { let latest - Object.keys(versions0).forEach((version) => { - if (version.match(/main|master|nightly|latest/g) == null) { - version = semver.coerce(version, { includePrerelease: true }) - latest = latest && semver.gt(latest, version) ? latest : version + Object.keys(versions0).forEach((v) => { + if (validVersion(v)) { + latest = latest && gt(latest, v) ? latest : v } }) - versions0.latest = latest.version + versions0.latest = latest const spec = maybeRemoveVPrefix(spec0) const altVersions = {} diff --git a/package-lock.json b/package-lock.json index ce56b686..62280577 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "@actions/exec": "1.1.1", "@actions/http-client": "2.1.0", "@actions/tool-cache": "2.0.1", - "semver": "7.5.4" + "semver": "7.6.2" }, "devDependencies": { "@vercel/ncc": "0.36.1", @@ -2358,12 +2358,9 @@ "dev": true }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "bin": { "semver": "bin/semver.js" }, @@ -2377,17 +2374,6 @@ "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", "dev": true }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/serialize-error": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", @@ -2869,11 +2855,6 @@ "node": ">=10" } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "node_modules/yaml-lint": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/yaml-lint/-/yaml-lint-1.7.0.tgz", diff --git a/package.json b/package.json index cd792bdc..6686828a 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "@actions/exec": "1.1.1", "@actions/http-client": "2.1.0", "@actions/tool-cache": "2.0.1", - "semver": "7.5.4" + "semver": "7.6.2" }, "devDependencies": { "@vercel/ncc": "0.36.1", diff --git a/src/setup-beam.js b/src/setup-beam.js index 30567106..e3460fb6 100644 --- a/src/setup-beam.js +++ b/src/setup-beam.js @@ -378,15 +378,34 @@ function isStrictVersion() { return getInput('version-type', false) === 'strict' } +function gt(left, right) { + return semver.gt(parseVersion(left), parseVersion(right)) +} + +function validVersion(v) { + return ( + v.match(/main|master|nightly|latest/g) == null && + !v.startsWith('a') && + !v.startsWith('b') + ) +} + +function parseVersion(v) { + v = v.includes('rc') ? v : v.split('.') + if (v instanceof Array) { + v = `${[v.shift(), v.shift(), v.shift()].join('.')}+${v.join('.')}` + } + return semver.coerce(v, { includePrerelease: true, loose: true }) +} + function getVersionFromSpec(spec0, versions0) { let latest - Object.keys(versions0).forEach((version) => { - if (version.match(/main|master|nightly|latest/g) == null) { - version = semver.coerce(version, { includePrerelease: true }) - latest = latest && semver.gt(latest, version) ? latest : version + Object.keys(versions0).forEach((v) => { + if (validVersion(v)) { + latest = latest && gt(latest, v) ? latest : v } }) - versions0.latest = latest.version + versions0.latest = latest const spec = maybeRemoveVPrefix(spec0) const altVersions = {} diff --git a/test/otp/ubuntu-24.04/builds.txt b/test/otp/ubuntu-24.04/builds.txt new file mode 100644 index 00000000..6ba07f58 --- /dev/null +++ b/test/otp/ubuntu-24.04/builds.txt @@ -0,0 +1,71 @@ + +OTP-24.3.4 0863bd30aabd035c83158c78046c5ffda16127e1 2024-04-26T03:41:09Z +OTP-24.3.4.1 325a5920aa26b7d4996f9397f3d62ea7db6a8b92 2024-04-26T03:39:33Z +OTP-24.3.4.10 9d378933e266fe5b1ef760cfdea51e370ab6f643 2024-04-26T02:55:31Z +OTP-24.3.4.11 a23ba70ef91a76470fcf3d1c60d3e9f641f05844 2024-04-26T02:53:05Z +OTP-24.3.4.12 27417cdfc528081a6795828032d8a13cc82048f9 2024-04-26T02:51:04Z +OTP-24.3.4.13 e7eaff0ebe5bffdf0a2f2d10d76efcbec92b0909 2024-04-26T02:49:39Z +OTP-24.3.4.14 92879e99b91efeed36ae9d8abaafe4d9552d06de 2024-04-26T02:39:46Z +OTP-24.3.4.15 85be379c3f1c0021ccb8d6ae9eb69f4e6712a150 2024-04-26T02:37:32Z +OTP-24.3.4.16 e1a54d023698359f0b562219e591f6263596c735 2024-04-26T02:35:14Z +OTP-24.3.4.17 e0001cd0fa553513ee8fee998a82a01ac9b7329b 2024-04-26T02:33:42Z +OTP-24.3.4.2 0251f542a2ef42ded2a146649d05ff9e7e457268 2024-04-26T03:29:08Z +OTP-24.3.4.3 70877dcee9c0aa9f5a39fb5af62da531a737cca0 2024-04-26T03:26:17Z +OTP-24.3.4.4 fc73c903cec66b46caa0afa4785d139092ad0485 2024-04-26T03:24:45Z +OTP-24.3.4.5 11940accce94c19cb3c6efbacba7dcd39e0736ec 2024-04-26T03:23:10Z +OTP-24.3.4.6 5e5963b8f71ace28c60d358f1d76ad472442019e 2024-04-26T03:12:08Z +OTP-24.3.4.7 6edaed6e23779057965231abd4e4b0421d566ec7 2024-04-26T03:09:20Z +OTP-24.3.4.8 f17ccd2dffb651599c9e55b1550ce45c0ba92af8 2024-04-26T03:07:19Z +OTP-24.3.4.9 68522e556c020de5fe7e20c4c082927a5d04d8e7 2024-04-26T03:05:47Z +OTP-25.0 4ed7957623e5ccbd420a09a506bd6bc9930fe93c 2024-04-26T02:16:30Z +OTP-25.0.1 cf18f250a40f33cc648d869eac833cd9aee86ed6 2024-04-26T02:16:12Z +OTP-25.0.2 ac0c9879c68b23278178a7afe738285b33ff1832 2024-04-26T02:14:16Z +OTP-25.0.3 89c04fb1836fb865565125a4d5880b93e784d856 2024-04-26T02:12:02Z +OTP-25.0.4 c028b855ee3f12c835ff3538a90ac5dbc155631b 2024-04-26T01:58:16Z +OTP-25.1 6efb5e31df6bc512ed6c466584ef15b846dcecab 2024-04-26T01:58:24Z +OTP-25.1.1 be6f05d34736fadf37328058f00e788cb88da61b 2024-04-26T01:56:43Z +OTP-25.1.2 38ad8e28421c745c06ef9bd55f6e6204cd1f15ef 2024-04-26T01:54:11Z +OTP-25.1.2.1 7dc04c755eef47b34f5763dcfc8d16913c1b7a08 2024-04-26T01:40:40Z +OTP-25.2 d6eb1c53d688a242bfb5e3b4febcea66c49c0fe7 2024-04-26T01:40:36Z +OTP-25.2.1 43361e793b63d57570a5a46da4a5cb4a1c3563c5 2024-04-26T01:39:05Z +OTP-25.2.2 8adae7eebbc791433d5f0f58daab558a8fbe847d 2024-04-26T01:36:16Z +OTP-25.2.3 07cf1c5caab19852ccf85ae7000d788913f3411d 2024-04-26T01:21:10Z +OTP-25.3 5400ccf243a31d664153a4b9ceb9de3edfce1e0e 2024-04-26T01:22:29Z +OTP-25.3.1 c487c0ea418315edd4a8614854e82c5359f4b8f8 2024-04-26T01:22:21Z +OTP-25.3.2 0418c10ec37fa374867eae49f2f776c72c55d623 2024-04-26T01:18:13Z +OTP-25.3.2.1 3f0a5f136aa72f785ca86df64c4384db2e9c8bbb 2024-04-26T01:01:44Z +OTP-25.3.2.10 733439dc03b21ea92c096b6cc31befc133cd9081 2024-04-26T00:06:15Z +OTP-25.3.2.11 e1e117f0e95cb373f9bd5e159e54e6ffae1bb32b 2024-04-26T00:06:20Z +OTP-25.3.2.12 cee6d023f30d5cedb4eaa2b1384b49597a9bde66 2024-05-02T14:29:24Z +OTP-25.3.2.2 ba144817d5478b15a50ef0c398d925232005277e 2024-04-26T00:58:37Z +OTP-25.3.2.3 da340fd39fc6550b1432662ffa55c3b6da3db41f 2024-04-26T00:58:30Z +OTP-25.3.2.4 9e6d04dd18bef6136fc2b45cc60fe1348c4664a7 2024-04-26T00:54:26Z +OTP-25.3.2.5 5cb360d524a5777ed4243ac03a0162a95f545a16 2024-04-26T00:37:15Z +OTP-25.3.2.6 fd0e27a5c1ca7afde30b10c1b33a35ebb3313fd1 2024-04-26T00:32:42Z +OTP-25.3.2.7 d3c9eeb17042e5567337e1f87c924b25186daae0 2024-04-26T00:32:39Z +OTP-25.3.2.8 c08f5acfb4e47933d8128d0b41b01487d0eb97a1 2024-04-26T00:29:09Z +OTP-25.3.2.9 68fa9c42426380772784eed26d24747bfee38fcc 2024-04-26T00:12:26Z +OTP-26.0 8c0ea6bd3306cfd6ca19730d180a2a93a716e1ee 2024-04-26T04:08:34Z +OTP-26.0-rc1 127026003180a834e9fa5d5919c824a184faeb92 2024-04-26T00:03:12Z +OTP-26.0-rc2 dac89a6acc1a93a615930ca18f1dbf4e9323b038 2024-04-25T23:47:30Z +OTP-26.0-rc3 1f897adc9df5e0de5d5a85633a8629a7e45ddeab 2024-04-25T23:39:30Z +OTP-26.0.1 b54b86ad4f1253f46fd4552a73923756660c1d53 2024-04-25T23:34:51Z +OTP-26.0.2 d051172925a5c84b2f21850a188a533f885f201c 2024-04-25T23:20:37Z +OTP-26.1 e962af35263618665c1df57df5135c0c703ad502 2024-04-25T23:10:07Z +OTP-26.1.1 2bdd30b872ab91dc376998d2c417f2a8b514d1aa 2024-04-25T23:09:59Z +OTP-26.1.2 c41d424db42ba84b72f3e25167470c3555723d87 2024-04-25T23:06:27Z +OTP-26.2 7fc0898502cb3370b7c6d523a7393cd101808493 2024-04-25T22:52:52Z +OTP-26.2.1 ca8b893f9d5bdd0957b78514ba523032e762c644 2024-04-25T22:40:28Z +OTP-26.2.2 b83df13eec5446beab06dd24315d37a5b0633fd2 2024-04-25T22:40:25Z +OTP-26.2.3 928d03e6da416208fce7b9a7dbbfbb4f25d26c37 2024-04-25T22:38:03Z +OTP-26.2.4 e26c5206dc98ec1b8f978fceaa61fd1354266ccb 2024-04-25T22:24:21Z +OTP-26.2.5 412bff5196fc0ab88a61fe37ca30e5226fc7872d 2024-05-02T16:20:10Z +OTP-27.0 601a012837ea0a5c8095bf24223132824177124d 2024-05-20T09:54:18Z +OTP-27.0-rc1 b74bd21d5cb52e0fdc5ea321439c428783feea23 2024-04-25T22:11:25Z +OTP-27.0-rc2 e651174c569694c92b1794ddd0a1a4a199610091 2024-04-25T22:11:21Z +OTP-27.0-rc3 5df3992d695da4d7e8777cf7605279ce9d131f1c 2024-04-25T22:10:15Z +maint 98fa13790b44ad84db1a294aa7e682111a331f37 2024-06-11T08:07:32Z +maint-25 cee6d023f30d5cedb4eaa2b1384b49597a9bde66 2024-05-02T14:04:45Z +maint-26 412bff5196fc0ab88a61fe37ca30e5226fc7872d 2024-05-02T15:52:49Z +maint-27 601a012837ea0a5c8095bf24223132824177124d 2024-05-20T15:17:40Z +master 0b1bab25dbabb6c19319f3373c5a6dfa74667210 2024-06-11T08:07:29Z diff --git a/test/setup-beam.test.js b/test/setup-beam.test.js index d5f8905b..ab4d3d4b 100644 --- a/test/setup-beam.test.js +++ b/test/setup-beam.test.js @@ -19,6 +19,7 @@ const matrix = { 'ubuntu-18.04': parseBuild('test/otp/ubuntu-18.04/builds.txt'), 'ubuntu-20.04': parseBuild('test/otp/ubuntu-20.04/builds.txt'), 'ubuntu-22.04': parseBuild('test/otp/ubuntu-22.04/builds.txt'), + 'ubuntu-24.04': parseBuild('test/otp/ubuntu-24.04/builds.txt'), windows: parseReleases('test/otp/releases.json'), }, elixir: parseBuild('test/elixir/builds.txt'), @@ -435,7 +436,7 @@ async function testGetVersionFromSpec() { '12.1.2.3', '12.1.2.3.0', ] - const versions = {} + let versions = {} versions0.forEach((version) => { versions[version] = version }) @@ -568,32 +569,74 @@ async function testGetVersionFromSpec() { simulateInput('version-type', before) spec = 'latest' - expected = '24.0.0' + expected = '24.0' got = setupBeam.getVersionFromSpec(spec, versions) assert.deepStrictEqual(got, expected) + versions = { + '27.0.0-rc3': '27.0.0-rc3', + '27.0.0-rc2': '27.0.0-rc2', + } spec = 'latest' - got = setupBeam.getVersionFromSpec(spec, [ - { '22.3.4.2.1.0.1': '22.3.4.2.1.0.1', '22.3.4.2.1.0.0': '22.3.4.2.1.0.0' }, - ]) - assert.deepStrictEqual(got, '22.3.4.2.1.0.1') + expected = '27.0.0-rc3' + got = setupBeam.getVersionFromSpec(spec, versions) + assert.deepStrictEqual(got, expected) - got = setupBeam.getVersionFromSpec(spec, matrix.otp['ubuntu-18.04']) - assert.deepStrictEqual(got, '26.2.1') + versions = { + '27.0.0-rc3': '27.0.0-rc3', + '27.0.0-rc2': '27.0.0-rc2', + '27.0.0': '27.0.0', + } + spec = 'latest' expected = '27.0.0' + got = setupBeam.getVersionFromSpec(spec, versions) + assert.deepStrictEqual(got, expected) + + versions = { '25.3.2.8': '25.3.2.8', '25.3.2.12': '25.3.2.12' } + spec = 'latest' + expected = '25.3.2.12' + got = setupBeam.getVersionFromSpec(spec, versions) + assert.deepStrictEqual(got, expected) + + spec = 'latest' + expected = 'OTP-26.2.1' + got = setupBeam.getVersionFromSpec(spec, matrix.otp['ubuntu-18.04']) + assert.deepStrictEqual(got, expected) + + spec = 'latest' + expected = 'OTP-27.0-rc3' got = setupBeam.getVersionFromSpec(spec, matrix.otp['ubuntu-20.04']) assert.deepStrictEqual(got, expected) + + spec = 'latest' + expected = 'OTP-27.0-rc3' got = setupBeam.getVersionFromSpec(spec, matrix.otp['ubuntu-22.04']) assert.deepStrictEqual(got, expected) + + spec = 'latest' + expected = 'OTP-27.0' + got = setupBeam.getVersionFromSpec(spec, matrix.otp['ubuntu-24.04']) + assert.deepStrictEqual(got, expected) + + spec = 'latest' + expected = '27.0-rc3' got = setupBeam.getVersionFromSpec(spec, matrix.otp.windows) assert.deepStrictEqual(got, expected) + spec = 'latest' + expected = 'v1.16.2' got = setupBeam.getVersionFromSpec(spec, matrix.elixir) - assert.deepStrictEqual(got, '1.16.2') + assert.deepStrictEqual(got, expected) + + spec = 'latest' + expected = 'v1.1.0' got = setupBeam.getVersionFromSpec(spec, matrix.gleam) - assert.deepStrictEqual(got, '1.1.0') + assert.deepStrictEqual(got, expected) + + spec = 'latest' + expected = '3.23.0' got = setupBeam.getVersionFromSpec(spec, matrix.rebar3) - assert.deepStrictEqual(got, '6.0.0') + assert.deepStrictEqual(got, expected) } async function testParseVersionFile() { @@ -610,8 +653,7 @@ async function testParseVersionFile() { erlang ref:v${erlang}# comment, no space, and ref:v elixir ref:${elixir} # comment, with space and ref: not-gleam 0.23 # not picked up -gleam ${gleam} -rebar ${rebar3}` +gleam ${gleam} \nrebar ${rebar3}` const filename = 'test/.tool-versions' fs.writeFileSync(filename, toolVersions) process.env.GITHUB_WORKSPACE = ''