aaronparker / evergreen

Create evergreen Windows image build pipelines with the latest version numbers and download URLs for common applications
http://stealthpuppy.com/evergreen/
MIT License
369 stars 63 forks source link

[Feature]: Enforce valid .NET version types in evergreen objects #717

Open BronsonMagnan opened 1 month ago

BronsonMagnan commented 1 month ago

What is your feature request?

It would be nice if Evergreen could filter scrub malformed version numbers that do not conform to the .NET version class. Two examples in the list of Evergreen applications now are these: X:\evergreen\stealthpuppyWindowsCustomisedDefaults\2406.01.173\x86 (the leading 0 in the minor version number is not permitted and should be cleaned up) X:\evergreen\QGIS\latest\3.38.0-1 (non '.' separators are not permitted) These types of issues cause problems for automation solutions that are leveraging the .NET version class for performing comparisons. I think extending that class on the developer side is a bridge too far, and the ideally the problem should be fixed at the supplier, but a compromise would be to have Evergreen filter and replace non conforming version numbers

Have you tested against the current version?

Have you reviewed the documentation?

BronsonMagnan commented 1 month ago

OneDrive also does this: X:\evergreen\MicrosoftOneDrive\Enterprise\24.010.0114.0003\x64

BronsonMagnan commented 1 month ago

Arcrobat does leading zeros. X:\evergreen\AdobeAcrobatProStdDC\24.002.20857\x64

BronsonMagnan commented 1 month ago

A sample for the filter could look like this:

function Format-Version () {
     [cmdletBinding()]
     [OutputType([string[]])]
     param(
          [parameter(valueFromPipeLine=$true)]
          [string[]]$Version
     )
     begin {
          $badSeparator = "[\d+\.]{0,2}(\d+([\-\/\+\~\:\\\\_])\d+)[\.\d+]{0,2}"
          $leadingZeros = "\b(0\d+|(\d+\.0\d+)|(\d+\.\d+\.0\d+)|(\d+\.\d+\.\d+\.0\d+)|(\d+\.0\d+\.\d+)|(\d+\.0\d+\.\d+\.0\d+)|(\d+\.\d+\.0\d+\.\d+))\b"
     }
     process {
          foreach ($Item in $Version) {
               if ($Item -match $badSeparator) { 
                    $badChar = $matches[2]
                    $Item = $Item.replace($badchar,'.')
                    $Item = Format-Version -Version $Item
               }
               if ($Item -match $leadingZeros) { 
                    $Item = ([Version]$Item).tostring()
               }
               Write-Output $item
          }

     }
     end {

     }
}
JimMoyle commented 1 month ago

I'd say reporting back the unscrubbed number is best and then consumers could scrub themselves as you demonstrate if needed.

BronsonMagnan commented 1 month ago

Jim do you have to deal with this problem in production? If so, how are you handling it?

JimMoyle commented 1 month ago

I'd generally normalise the version number after getting it to do comparisons later, but the reason I say don't do it in evergreen is you never know what people have written, they may be parsing the number any unusual way on a per app basis when they are consuming the output of evergreen.

It may be that someone at the ISV decides randomly to put the string 'release' in the version or do dot versions in 6 places. There are so many failure cases that you can't build a regex for, and they are totally out of the control of Evergreen itself.

You could try type conversion, then build regex replacements something like the below in a catch block if it fails, maybe adding your regex too, but I'd say it will always have issues.

$verCount = $Version.ToString().Split('.').Count
 switch ($true) {
     { $verCount -eq 4 } { $outputVer = $Version ;break}
     { $verCount -lt 4 } { 
          $outputVer = (4 - $verCount)..1 | ForEach-Object {
               $Version.ToString() + '.0'
           } 
                break
     }
     { $verCount -gt 4 } {
          $outputVer = $Version.ToString() -replace "^(\d+(?:\.\d+){0,3})(?:\.\d+)*$", $matches[1]
          break
     }
            Default {}
}

I'd say this would work mostly, but the issues raised on this repo would significantly increase when vendors just did weird shit with their versioning.

Given the lack of control over the source data I'd just pass it through to the module user.

Given it's not my decision, it's Aaron's, but that's what I'd do in his position.

BronsonMagnan commented 1 month ago

they may be parsing the number any unusual way on a per app basis when they are consuming the output of evergreen

I would say that this is the direct result of unsanitized version numbers. At least that is my case.

aaronparker commented 1 month ago

I'm hesitant to modify the version number output as it comes from the vendor too much. Below is an example of some of the version numbers - some of these could be fixed up, .e.g removing jdk, and v but these examples are on a per-app basis.

Some of the examples are obviously terrible version numbers - how do we determine how they should be modified?

8u422+6
22.0.1+8
v22-build1
21.0.4.7.1
17.0.12.7.1
jdk-11.0.23+9
7.1.1-35
2024b
129.0b3
2023.2.20f1
aaronparker commented 1 month ago

We could take that Format-Version example and include it as a function in Evergreen, so that you could run:

Get-EvergreenApp -Name "MicrosoftOneDrive" | Format-Version

DanGough commented 1 month ago

I do my own version sanitisation so that I can be in control of how it works and have the ability to quickly fix stuff when vendors do something unusual. Also because I get versions from multiple sources other than Evergreen. But it would definitely be of benefit to have something built in too.

Rather than returning a string that can be cast as a version, how about returning a VersionObject property that has already been cast?

BTW I also convert to a SQLVersion, which pads out each field to have up to 9 digits, which enables version sorting and comparison in SQL statements when these values are stored in a database. E.g. 1.2.3 becomes 100000000200000000300000000000000000. Not totally relevant to this topic, but just sharing an idea I had that solved a problem of mine!

BronsonMagnan commented 1 month ago

Get-EvergreenApp -Name "MicrosoftOneDrive" | Format-Version | Save-EvergreenApp ... would be perfect, sweet normalization.

BronsonMagnan commented 1 month ago

Aaron, probably a good idea to have the format function move the original vendor version to a new field in the object, in case it needed to be referenced for some reason. I imagine users will do filtering cmdlets after the format version step. Replacing the original version field seems like the way to go though, so the least amount of stuff needs to change.

BronsonMagnan commented 1 month ago

I figured this out, it maintains all sortability, and normalized to a 4 segment version number

function Normalize-Segment {
    param (
        [string]$segment
    )

    if ($segment -match '^\d+$') {
        return [int]$segment
    } else {
        $normalized = 0
        foreach ($char in $segment.ToCharArray()) {
            $normalized = $normalized * 100 + [int][char]$char
        }
        return $normalized
    }
}

function Convert-ToStandardVersion {
    param (
        [string]$version
    )

    $segments = $version -split '[.\-_+]'
    $normalizedSegments = @()

    foreach ($segment in $segments) {
        $normalizedSegments += @(Normalize-Segment -segment $segment)
    }

    if ($normalizedSegments.Count -gt 4) {
        $normalizedSegments = $normalizedSegments[0..2] + ($normalizedSegments[3..($normalizedSegments.Count - 1)] | Measure-Object -Sum).Sum
    }

    while ($normalizedSegments.Count -lt 4) {
        $normalizedSegments += 0
    }

    return ($normalizedSegments -join ".")
}

# Example
Convert-ToStandardVersion -version "8u422+6"         #5717525050.6.0.0 
Convert-ToStandardVersion -version "22.0.1+8"        #22.0.1.8 
Convert-ToStandardVersion -version "v22-build1"      #1185050.991806090049.0.0 
Convert-ToStandardVersion -version "21.0.4.7.1"      #21.0.4.8 
Convert-ToStandardVersion -version "17.0.12.7.1"     #17.0.12.8 
Convert-ToStandardVersion -version "jdk-11.0.23+9"   #1070107.11.0.32 
Convert-ToStandardVersion -version "7.1.1-35"        #7.1.1.35 
Convert-ToStandardVersion -version "2024b"           #5048505298.0.0.0 
Convert-ToStandardVersion -version "129.0b3"         #129.489851.0.0 
Convert-ToStandardVersion -version "2023.2.20f1"     #2023.2.50490249.0