Commit 3371caf1 authored by Pierre Smeyers's avatar Pierre Smeyers
Browse files

fix(version): own Version impl to compare DT server version

Fixes #1
parent 3582314f
Loading
Loading
Loading
Loading
+99 −1
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@ import argparse
import glob
import json
import os
import re
import ssl
import sys
from enum import Enum
@@ -13,7 +14,6 @@ from typing import Optional
import requests
from cyclonedx.model.bom import Bom
from cyclonedx.schema import SchemaVersion
from packaging.version import Version

from sbom_scanner import sbom_utils
from sbom_scanner.AnsiColors import AnsiColors
@@ -90,6 +90,104 @@ class DtProjectDef:
        return self.definition.split("@")[1] if "@" in self.definition else None


class Version:
    def __init__(self, version_str):
        self.version_str = version_str
        self.major, self.minor, self.patch, self.prerelease, self.build = self._parse(
            version_str
        )

    def _parse(self, version_str):
        regex = r"^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<build>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"
        match = re.match(regex, version_str)
        if match:
            major = int(match.group("major"))
            minor = int(match.group("minor"))
            patch = int(match.group("patch"))
            prerelease = match.group("prerelease")
            build = match.group("build")
            return major, minor, patch, prerelease, build
        else:
            raise ValueError(f"Invalid semantic version: {version_str}")

    def __str__(self):
        version_str = f"{self.major}.{self.minor}.{self.patch}"
        if self.prerelease:
            version_str += f"-{'.'.join(self.prerelease)}"
        if self.build:
            version_str += f"+{'.'.join(self.build)}"
        return version_str

    def __lt__(self, other):
        return self._compare(other) < 0

    def __le__(self, other):
        return self._compare(other) <= 0

    def __eq__(self, other):
        return self._compare(other) == 0

    def __ge__(self, other):
        return self._compare(other) >= 0

    def __gt__(self, other):
        return self._compare(other) > 0

    def __ne__(self, other):
        return self._compare(other) != 0

    def _compare(self, other):
        if not isinstance(other, Version):
            other = Version(str(other))

        if self.major != other.major:
            return 1 if self.major > other.major else -1
        if self.minor != other.minor:
            return 1 if self.minor > other.minor else -1
        if self.patch != other.patch:
            return 1 if self.patch > other.patch else -1

        # Handle pre-release versions
        if self.prerelease and other.prerelease:
            self_prerelease = [
                self._parse_prerelease(x) for x in self.prerelease.split(".")
            ]
            other_prerelease = [
                self._parse_prerelease(x) for x in other.prerelease.split(".")
            ]
            for i in range(min(len(self_prerelease), len(other_prerelease))):
                if self_prerelease[i] != other_prerelease[i]:
                    return 1 if self_prerelease[i] > other_prerelease[i] else -1
            return 1 if len(self_prerelease) > len(other_prerelease) else -1
        elif self.prerelease:
            return -1
        elif other.prerelease:
            return 1

        # Handle build metadata
        if self.build and other.build:
            self_build = [int(x) if x.isdigit() else x for x in self.build.split(".")]
            other_build = [int(x) if x.isdigit() else x for x in other.build.split(".")]
            for i in range(min(len(self_build), len(other_build))):
                if self_build[i] != other_build[i]:
                    return 1 if self_build[i] > other_build[i] else -1
            return 1 if len(self_build) > len(other_build) else -1
        elif self.build:
            return 1
        elif other.build:
            return -1

        return 0

    def _parse_prerelease(self, prerelease_str):
        digits = "".join(c for c in prerelease_str if c.isdigit())
        alpha = "".join(c for c in prerelease_str if not c.isdigit())
        if digits:
            return int(digits), alpha
        else:
            return 0, alpha


class Scanner:
    def __init__(
        self,