Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 84 additions & 5 deletions plugins/filters/compareMultiSemver/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,69 @@
* @license MIT
*/

/**
* Compares two software version numbers (e.g., "1.2.1" or "1.2b") and determines the type of version change.
* The first version to be compared, and the second are passed as argument 1 and 2 or as array of 2 items.
* When V1 > V2 the it means and upgrade.
* @param {string[]} versions - V1 and V2 in Semver format
* @returns {string} It returns a string of either:
* 'major' if the major version is incremented.
* 'minor' if the minor version is incremented.
* 'patch' if the patch version is incremented.
* 'downgrade' if the second version is lower than the first.
* 'equal' if both versions are equal.
* 'error' if the comparison is abnormal or cannot be determined.
*/
const compareSemver = (v1, v2) => {
const lexicographical = false;
const zeroExtend = true;

// support array as input
if (Array.isArray(v1) && v2 === undefined) {
[v1, v2] = v1; // Destructure the first two elements of the array into v1 and v2
}

let v1parts = (v1 || "0").split('.');
let v2parts = (v2 || "0").split('.');

const isValidPart = x => lexicographical ? /^\d+[A-Za-zαß]*$/.test(x) : /^\d+[A-Za-zαß]?$/.test(x);

if (!v1parts.every(isValidPart) || !v2parts.every(isValidPart)) {
return 'error';
}

if (zeroExtend) {
const maxLength = Math.max(v1parts.length, v2parts.length);
v1parts = [...v1parts, ...Array(maxLength - v1parts.length).fill("0")];
v2parts = [...v2parts, ...Array(maxLength - v2parts.length).fill("0")];
}

const convertPart = x => {
const match = /[A-Za-zαß]/.exec(x);
return Number(match ? x.replace(match[0], "." + x.charCodeAt(match.index)) : x);
Comment on lines +55 to +56
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🐞 Bug - Inconsistent Prerelease Handling: Implement proper semver prerelease comparison logic or use a standardized approach for handling alphabetic version suffixes instead of character codes.

Suggested change
const match = /[A-Za-zαß]/.exec(x);
return Number(match ? x.replace(match[0], "." + x.charCodeAt(match.index)) : x);
const match = /^(\d+)([A-Za-zαß]*)$/.exec(x);
if (!match) return Number(x);
const numeric = Number(match[1]);
const alpha = match[2];
if (!alpha) return numeric;
// Convert alphabetic suffix to a small decimal increment for proper ordering
// Each character contributes progressively smaller values to maintain order
let alphaValue = 0;
for (let i = 0; i < alpha.length; i++) {
const charValue = alpha.charCodeAt(i) - 96; // normalize a=1, b=2, etc.
alphaValue += charValue / Math.pow(100, i + 1); // decimal fractions
}
return numeric + alphaValue;

};

if (!lexicographical) {
v1parts = v1parts.map(convertPart);
v2parts = v2parts.map(convertPart);
}

const compareSemver = require('../compareSemver/index.js');
for (let i = 0; i < v1parts.length; i++) {
if (v1parts[i] !== v2parts[i]) {
if (v1parts[i] < v2parts[i]) {
return 'downgrade';
}
switch (i) {
case 0: return 'major';
case 1: return 'minor';
case 2: return 'patch';
default: return 'error';
}
}
}

return 'equal';
}

module.exports = (listOfPairs) => {

Expand All @@ -42,7 +103,25 @@ module.exports = (listOfPairs) => {


const compareMultiSemver = require('./index.js');
console.assert(compareMultiSemver([["1.2.3", "1.2.1"], ["1.3.1", "1.2.3"]]) === 'minor', `compareSemver([["1.2.3", "1.2.1"], ["1.3.1", "1.2.3"]]) == 'minor'`);
console.assert(compareMultiSemver([["1.2.3", "0.2.1"], ["1.3.1", "1.2.3"]]) === 'major', `compareMultiSemver([["1.2.3", "0.2.1"], ["1.3.1", "1.2.3"]]) === 'major'`);
console.assert(compareMultiSemver([["2.2.3", "0.2.1"], ["1.3.1", "1.2.3"]]) === 'major', `compareMultiSemver([["2.2.3", "0.2.1"], ["1.3.1", "1.2.3"]]) === 'major'`);
console.assert(compareMultiSemver([["1.2.3", "1.2.1"], ["1.2.4", "1.2.3"]]) === 'patch', `compareMultiSemver([["1.2.3", "1.2.1"], ["1.2.4", "1.2.3"]]) === 'patch'`);

// Test 1: Should return 'minor'
const test1 = compareMultiSemver([["1.2.3", "1.2.1"], ["1.3.1", "1.2.3"]]);
console.assert(test1 === 'minor', `Test 1 failed: compareMultiSemver([["1.2.3", "1.2.1"], ["1.3.1", "1.2.3"]]) returned '${test1}', expected 'minor'`);
console.log('✓ Test 1 passed: compareMultiSemver([["1.2.3", "1.2.1"], ["1.3.1", "1.2.3"]]) === "minor"');

// Test 2: Should return 'major'
const test2 = compareMultiSemver([["1.2.3", "0.2.1"], ["1.3.1", "1.2.3"]]);
console.assert(test2 === 'major', `Test 2 failed: compareMultiSemver([["1.2.3", "0.2.1"], ["1.3.1", "1.2.3"]]) returned '${test2}', expected 'major'`);
console.log('✓ Test 2 passed: compareMultiSemver([["1.2.3", "0.2.1"], ["1.3.1", "1.2.3"]]) === "major"');

// Test 3: Should return 'major'
const test3 = compareMultiSemver([["2.2.3", "0.2.1"], ["1.3.1", "1.2.3"]]);
console.assert(test3 === 'major', `Test 3 failed: compareMultiSemver([["2.2.3", "0.2.1"], ["1.3.1", "1.2.3"]]) returned '${test3}', expected 'major'`);
console.log('✓ Test 3 passed: compareMultiSemver([["2.2.3", "0.2.1"], ["1.3.1", "1.2.3"]]) === "major"');

// Test 4: Should return 'patch'
const test4 = compareMultiSemver([["1.2.3", "1.2.1"], ["1.2.4", "1.2.3"]]);
console.assert(test4 === 'patch', `Test 4 failed: compareMultiSemver([["1.2.3", "1.2.1"], ["1.2.4", "1.2.3"]]) returned '${test4}', expected 'patch'`);
console.log('✓ Test 4 passed: compareMultiSemver([["1.2.3", "1.2.1"], ["1.2.4", "1.2.3"]]) === "patch"');

console.log('\n🎉 All tests passed!');