From cd44d669ea327d74961e47d310b551d115d30773 Mon Sep 17 00:00:00 2001 From: Alex Tumanov <6143578+oleksii-tumanov@users.noreply.github.com> Date: Sat, 18 Jul 2026 04:07:35 -0500 Subject: [PATCH] Fix versionLT for wrapped CD plugin versions --- jenkins-support | 54 ++++++++++++++++++++++++++++++++++++++- jenkins-support.psm1 | 43 ++++++++++++++++++++++++++++++- tests/functions.Tests.ps1 | 17 ++++++++++++ tests/functions.bats | 12 +++++++++ 4 files changed, 124 insertions(+), 2 deletions(-) diff --git a/jenkins-support b/jenkins-support index 3f5d064a53..50bbbeada6 100755 --- a/jenkins-support +++ b/jenkins-support @@ -28,9 +28,37 @@ substitute_env_vars() { echo "${content}" > "${file}" } +# compare if numeric dotted version1 < numeric dotted version2 +numericVersionLT() { + local version1 version2 i part1 part2 max + local -a parts1 parts2 + version1=$1 + version2=$2 + + IFS=. read -r -a parts1 <<< "$version1" + IFS=. read -r -a parts2 <<< "$version2" + max=${#parts1[@]} + if (( ${#parts2[@]} > max )); then + max=${#parts2[@]} + fi + + for (( i = 0; i < max; i++ )); do + part1=${parts1[i]:-0} + part2=${parts2[i]:-0} + if (( 10#$part1 < 10#$part2 )); then + return 0 + fi + if (( 10#$part1 > 10#$part2 )); then + return 1 + fi + done + + return 1 +} + # compare if version1 < version2 versionLT() { - local normalized_version1 normalized_version2 first_part_of_1 other_part_of_1 first_char_other_part_of_1 first_part_of_2 other_part_of_2 first_char_other_part_of_2 + local normalized_version1 normalized_version2 first_part_of_1 other_part_of_1 first_char_other_part_of_1 first_part_of_2 other_part_of_2 first_char_other_part_of_2 numeric_prefix_1 numeric_prefix_2 jenkins_suffix_1 jenkins_suffix_2 # Quick check for equality if [ "$1" = "$2" ]; then @@ -75,6 +103,30 @@ versionLT() { fi fi + # Semver-prefix + Jenkins CD release suffix special case. + # Ex: 9.10-211.v7d13903b_a_d89 < 9.10.1-216.va_9256d3b_844b_ + # `sort --version-sort` compares 9.10.211 with 9.10.1.216 after the + # generic "-" -> "." normalization below, so it treats the older upstream + # 9.10 line as newer than 9.10.1. When both sides have a numeric wrapped + # component version prefix before the Jenkins CD release suffix, compare + # that prefix first and fall back to the existing behavior only when the + # prefixes are equal. Keep non-CD suffixes on the existing path. + # Tracked at https://github.com/jenkinsci/docker/issues/2361 + if [[ "$1" == *-* && "$2" == *-* ]]; then + numeric_prefix_1=${1%%-*} + numeric_prefix_2=${2%%-*} + jenkins_suffix_1=${1#*-} + jenkins_suffix_2=${2#*-} + if [[ "$numeric_prefix_1" =~ ^[0-9]+(\.[0-9]+)*$ && "$numeric_prefix_2" =~ ^[0-9]+(\.[0-9]+)*$ && "$jenkins_suffix_1" =~ ^[0-9]+\.v.+$ && "$jenkins_suffix_2" =~ ^[0-9]+\.v.+$ ]]; then + if numericVersionLT "$numeric_prefix_1" "$numeric_prefix_2"; then + return 0 + fi + if numericVersionLT "$numeric_prefix_2" "$numeric_prefix_1"; then + return 1 + fi + fi + fi + if [ "$normalized_version1" = "$(printf '%s\n%s\n' "$normalized_version1" "$normalized_version2" | sort --version-sort | head -n1)" ]; then return 0 else diff --git a/jenkins-support.psm1 b/jenkins-support.psm1 index b7fe4185a4..39455ba3d4 100644 --- a/jenkins-support.psm1 +++ b/jenkins-support.psm1 @@ -1,3 +1,23 @@ +# compare if numeric dotted version1 < numeric dotted version2 +function Compare-NumericVersionLessThan([string] $version1 = '', [string] $version2 = '') { + $version1Parts = $version1.Split('.') + $version2Parts = $version2.Split('.') + $maxLength = [Math]::Max($version1Parts.Length, $version2Parts.Length) + + for ($i = 0; $i -lt $maxLength; $i++) { + $version1part = if ($i -lt $version1Parts.Length) { [int64]$version1Parts[$i] } else { 0 } + $version2part = if ($i -lt $version2Parts.Length) { [int64]$version2Parts[$i] } else { 0 } + + if ($version1part -lt $version2part) { + return $true + } + if ($version1part -gt $version2part) { + return $false + } + } + + return $false +} # compare if version1 < version2 function Compare-VersionLessThan([string] $version1 = '', [string] $version2 = '') { @@ -13,6 +33,27 @@ function Compare-VersionLessThan([string] $version1 = '', [string] $version2 = ' $version1Parts = $normalizedVersion1.Split('.') $version2Parts = $normalizedVersion2.Split('.') + # Semver-prefix + Jenkins CD release suffix special case. + # Ex: 9.10-211.v7d13903b_a_d89 < 9.10.1-216.va_9256d3b_844b_ + # The generic "-" -> "." normalization compares 9.10.211 with 9.10.1.216, + # so it treats the older upstream 9.10 line as newer than 9.10.1. + # Keep non-CD suffixes on the existing path. + # Tracked at https://github.com/jenkinsci/docker/issues/2361 + if (($version1 -like '*-*') -and ($version2 -like '*-*')) { + $numericPrefix1 = ($version1 -split '-', 2)[0] + $numericPrefix2 = ($version2 -split '-', 2)[0] + $jenkinsSuffix1 = ($version1 -split '-', 2)[1] + $jenkinsSuffix2 = ($version2 -split '-', 2)[1] + if (($numericPrefix1 -match '^[0-9]+(\.[0-9]+)*$') -and ($numericPrefix2 -match '^[0-9]+(\.[0-9]+)*$') -and ($jenkinsSuffix1 -match '^[0-9]+\.v.+$') -and ($jenkinsSuffix2 -match '^[0-9]+\.v.+$')) { + if (Compare-NumericVersionLessThan $numericPrefix1 $numericPrefix2) { + return $true + } + if (Compare-NumericVersionLessThan $numericPrefix2 $numericPrefix1) { + return $false + } + } + } + # Compare major versions if ($version1Parts[0] -lt $version2parts[0]) { return $true @@ -251,4 +292,4 @@ function Copy-ReferenceFile($file) { Add-Content -Path $COPY_REFERENCE_FILE_LOG -Value "$action $rel : $reason" } } -} \ No newline at end of file +} diff --git a/tests/functions.Tests.ps1 b/tests/functions.Tests.ps1 index 73d728ed56..a9a160dc44 100644 --- a/tests/functions.Tests.ps1 +++ b/tests/functions.Tests.ps1 @@ -62,6 +62,23 @@ Describe "[functions > $global:TEST_TAG] Check-VersionLessThan" -Skip:(-not $glo docker run --rm $global:SUT_IMAGE "Import-Module -DisableNameChecking -Force C:/ProgramData/Jenkins/jenkins-support.psm1 ; if(`$(Compare-VersionLessThan '4106.4108.v841a_e1819d4d' '4151.v5406e29e3c90')) { exit 0 } else { exit -1 }" $LastExitCode | Should -Be 0 } + ## Real world examples from https://github.com/jenkinsci/docker/issues/2361 + It 'has left side greater (apache-httpcomponents-client-5-api-plugin, semver prefix with CD release suffix)' { + docker run --rm $global:SUT_IMAGE "Import-Module -DisableNameChecking -Force C:/ProgramData/Jenkins/jenkins-support.psm1 ; if(`$(Compare-VersionLessThan '5.6.1-195.v65ffe15189a_d' '5.6-191.vb_47e2b_41c698')) { exit 0 } else { exit -1 }" + $LastExitCode | Should -Be -1 + } + It 'has right side greater (apache-httpcomponents-client-5-api-plugin, semver prefix with CD release suffix)' { + docker run --rm $global:SUT_IMAGE "Import-Module -DisableNameChecking -Force C:/ProgramData/Jenkins/jenkins-support.psm1 ; if(`$(Compare-VersionLessThan '5.6-191.vb_47e2b_41c698' '5.6.1-195.v65ffe15189a_d')) { exit 0 } else { exit -1 }" + $LastExitCode | Should -Be 0 + } + It 'has left side greater (asm-api-plugin, semver prefix with CD release suffix)' { + docker run --rm $global:SUT_IMAGE "Import-Module -DisableNameChecking -Force C:/ProgramData/Jenkins/jenkins-support.psm1 ; if(`$(Compare-VersionLessThan '9.10.1-216.va_9256d3b_844b_' '9.10-211.v7d13903b_a_d89')) { exit 0 } else { exit -1 }" + $LastExitCode | Should -Be -1 + } + It 'has right side greater (asm-api-plugin, semver prefix with CD release suffix)' { + docker run --rm $global:SUT_IMAGE "Import-Module -DisableNameChecking -Force C:/ProgramData/Jenkins/jenkins-support.psm1 ; if(`$(Compare-VersionLessThan '9.10-211.v7d13903b_a_d89' '9.10.1-216.va_9256d3b_844b_')) { exit 0 } else { exit -1 }" + $LastExitCode | Should -Be 0 + } } # Only test on Java 21, one JDK is enough to test all versions diff --git a/tests/functions.bats b/tests/functions.bats index 1a91e78dde..94c3694473 100644 --- a/tests/functions.bats +++ b/tests/functions.bats @@ -49,6 +49,18 @@ SUT_DESCRIPTION="${IMAGE}-functions" # role-strategy-plugin, reverse-direction run docker run --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 587.588.v850a_20a_30162 587.v2872c41fa_e51" assert_failure + ## https://github.com/jenkinsci/docker/issues/2361 + ## Semver-prefix + Jenkins CD release suffix cases: compare the numeric upstream prefix before the suffix. + # apache-httpcomponents-client-5-api-plugin + run docker run --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 5.6.1-195.v65ffe15189a_d 5.6-191.vb_47e2b_41c698" + assert_failure + run docker run --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 5.6-191.vb_47e2b_41c698 5.6.1-195.v65ffe15189a_d" + assert_success + # asm-api-plugin + run docker run --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 9.10.1-216.va_9256d3b_844b_ 9.10-211.v7d13903b_a_d89" + assert_failure + run docker run --rm $SUT_IMAGE bash -c "source /usr/local/bin/jenkins-support && versionLT 9.10-211.v7d13903b_a_d89 9.10.1-216.va_9256d3b_844b_" + assert_success } @test "[${SUT_DESCRIPTION}] permissions are propagated from override file" {