diff --git a/arcup/arcup b/arcup/arcup index 3590cd3..b6510ea 100755 --- a/arcup/arcup +++ b/arcup/arcup @@ -261,17 +261,24 @@ download_error() { version_gt() { [ "$1" = "$2" ] && return 1 - # Remove 'v' prefix if present - local ver1="${1#v}" - local ver2="${2#v}" - ver1="${ver1%%-*}" - ver2="${ver2%%-*}" - + # Remove 'v' prefix and strip build metadata (+...) up front. SemVer ignores + # build metadata for precedence, and stripping it first prevents a '-' inside + # build metadata (e.g. 1.0.0+build-1) from being misread as a prerelease. + local v1="${1#v}"; v1="${v1%%+*}" + local v2="${2#v}"; v2="${v2%%+*}" + + # Split into core "major.minor.patch" and optional prerelease. + local core1="${v1%%-*}" pre1="" + [ "$v1" = "$core1" ] || pre1="${v1#*-}" + local core2="${v2%%-*}" pre2="" + [ "$v2" = "$core2" ] || pre2="${v2#*-}" + + local major1 minor1 patch1 major2 minor2 patch2 IFS=. read -r major1 minor1 patch1 < lower precedence + [ -z "$id2" ] && return 0 # more identifiers -> higher precedence + [ "$id1" = "$id2" ] && continue + if [[ "$id1" =~ ^[0-9]+$ ]] && [[ "$id2" =~ ^[0-9]+$ ]]; then + [ "$id1" -gt "$id2" ] && return 0 + [ "$id1" -lt "$id2" ] && return 1 + elif [[ "$id1" =~ ^[0-9]+$ ]]; then + return 1 # numeric identifiers rank lower than alphanumeric + elif [[ "$id2" =~ ^[0-9]+$ ]]; then + return 0 + else + [[ "$id1" > "$id2" ]] && return 0 + [[ "$id1" < "$id2" ]] && return 1 + fi + done return 1 } diff --git a/arcup/version_gt_test.sh b/arcup/version_gt_test.sh new file mode 100644 index 0000000..4318e7d --- /dev/null +++ b/arcup/version_gt_test.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +# +# Tests for version_gt() in ./arcup (SemVer precedence, semver.org section 11). +# +# Usage: +# bash arcup/version_gt_test.sh +# +# It sources ./arcup with ARCUP_SKIP_MAIN=1 so only the function definitions load +# (the installer's main flow is skipped) and then exercises version_gt(). + +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +export ARCUP_SKIP_MAIN=1 +# shellcheck source=./arcup disable=SC1091 +source "$HERE/arcup" +# arcup runs under `set -euo pipefail`; relax it so the harness controls flow. +set +eu +o pipefail 2>/dev/null || true + +pass=0 +fail=0 + +# assert_gt A B -> expect version_gt to report A > B (exit 0) +assert_gt() { + if version_gt "$1" "$2"; then + pass=$((pass + 1)) + else + echo "FAIL: expected '$1' > '$2'" + fail=$((fail + 1)) + fi +} + +# assert_le A B -> expect version_gt to report NOT A > B (exit non-zero) +assert_le() { + if version_gt "$1" "$2"; then + echo "FAIL: expected NOT '$1' > '$2'" + fail=$((fail + 1)) + else + pass=$((pass + 1)) + fi +} + +# Reported bug (#205): prerelease vs stable and prerelease vs prerelease +assert_gt "0.3.0" "0.3.0-rc.1" +assert_le "0.3.0-rc.1" "0.3.0" +assert_gt "0.3.0-rc.2" "0.3.0-rc.1" +assert_le "0.3.0-rc.1" "0.3.0-rc.2" + +# Core major.minor.patch comparisons (with and without 'v' prefix) +assert_gt "1.0.0" "0.9.9" +assert_le "0.9.9" "1.0.0" +assert_gt "v1.2.4" "v1.2.3" +assert_le "1.2.3" "1.2.3" +assert_gt "1.3.0" "1.2.9" +assert_gt "2.0.0-rc.1" "1.9.9" + +# Full SemVer 11.4 precedence chain: +# 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta +# < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0 +assert_gt "1.0.0-alpha.1" "1.0.0-alpha" +assert_gt "1.0.0-alpha.beta" "1.0.0-alpha.1" +assert_gt "1.0.0-beta" "1.0.0-alpha.beta" +assert_gt "1.0.0-beta.2" "1.0.0-beta" +assert_gt "1.0.0-beta.11" "1.0.0-beta.2" +assert_gt "1.0.0-rc.1" "1.0.0-beta.11" +assert_gt "1.0.0" "1.0.0-rc.1" + +# Build metadata is ignored for precedence +assert_le "1.0.0+build.9" "1.0.0+build.1" +assert_le "1.0.0" "1.0.0+build.1" + +# Build metadata containing a hyphen must NOT be treated as a prerelease +assert_le "1.0.0+build-123" "1.0.0" +assert_le "1.0.0" "1.0.0+build-123" +assert_gt "1.0.1+build-1" "1.0.0" + +# Prerelease combined with build metadata: build metadata is ignored, so +# precedence is decided purely by the prerelease identifiers. +assert_le "1.0.0-rc.1+build-123" "1.0.0-rc.1" +assert_le "1.0.0-rc.1" "1.0.0-rc.1+build-123" +assert_gt "1.0.0-rc.2+build-1" "1.0.0-rc.1+build-99" + +echo "version_gt: ${pass} passed, ${fail} failed" +[ "${fail}" -eq 0 ]