[arch] fix: some dependencies not being matched during comparisons between numeric and alphanumeric versions

This commit is contained in:
Vinicius Moreira
2021-06-17 17:03:34 -03:00
parent abaffcb850
commit db5eeabfb8
8 changed files with 104 additions and 53 deletions

View File

@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [0.9.18]
### Fixes
- Arch
- install/upgrade/downgrade: some dependencies not being matched during comparisons between numeric and alphanumeric versions
## [0.9.17] 2021-06-16
### Improvements
- general: replacing subprocess commands to detect installed CLIs by Python faster calls (**shutil.which**)

View File

@@ -1,4 +1,4 @@
__version__ = '0.9.17'
__version__ = '0.9.18'
__app_name__ = 'bauh'
import os

View File

@@ -3,13 +3,11 @@ import traceback
from threading import Thread
from typing import Set, List, Tuple, Dict, Iterable, Optional
from packaging.version import parse as parse_version
from bauh.api.abstract.handler import ProcessWatcher
from bauh.gems.arch import pacman, message, sorting, confirmation
from bauh.gems.arch.aur import AURClient
from bauh.gems.arch.exceptions import PackageNotFoundException
from bauh.gems.arch.util import clean_version
from bauh.gems.arch.version import compare_versions
from bauh.view.util.translation import I18n
@@ -220,7 +218,7 @@ class DependenciesAnalyser:
matched_providers = set()
split_informed_dep = self.re_dep_operator.split(dep_exp)
try:
version_required = parse_version(clean_version(split_informed_dep[2]))
version_required = split_informed_dep[2]
exp_op = split_informed_dep[1] if split_informed_dep[1] != '=' else '=='
for p in providers:
@@ -230,9 +228,9 @@ class DependenciesAnalyser:
split_dep = self.re_dep_operator.split(provided_exp)
if len(split_dep) == 3 and split_dep[0] == dep_name:
version_provided = parse_version(clean_version(split_dep[2]))
version_provided = split_dep[2]
if eval('version_provided {} version_required'.format(exp_op)):
if compare_versions(version_provided, exp_op, version_required):
matched_providers.add(p)
break
@@ -270,17 +268,17 @@ class DependenciesAnalyser:
self.__raise_dependency_not_found(dep_exp, watcher)
else:
try:
dep_version = parse_version(clean_version(dep_info[0]['Version']))
dep_version = dep_info[0]['Version']
except:
traceback.print_exc()
self.__raise_dependency_not_found(dep_exp, watcher)
return self.__raise_dependency_not_found(dep_exp, watcher)
split_informed_dep = self.re_dep_operator.split(dep_exp)
try:
version_required = parse_version(clean_version(split_informed_dep[2]))
version_required = split_informed_dep[2]
exp_op = split_informed_dep[1] if split_informed_dep[1] != '=' else '=='
if eval('dep_version {} version_required'.format(exp_op)):
if compare_versions(dep_version, exp_op, version_required):
aur_deps.add(dep_name)
missing_deps.add((dep_name, 'aur'))
except:
@@ -334,20 +332,11 @@ class DependenciesAnalyser:
version_found = [p for p in provided_map if p.startswith(version_pattern)]
if version_found:
version_found = clean_version(version_found[0].split('=')[1])
version_required = clean_version(dep_split[2])
version_found = version_found[0].split('=')[1]
version_required = dep_split[2]
op = dep_split[1] if dep_split[1] != '=' else '=='
try:
version_found = parse_version(version_found)
version_required = parse_version(version_required)
op = dep_split[1] if dep_split[1] != '=' else '=='
match = eval('version_found {} version_required'.format(op))
except:
match = False
traceback.print_exc()
if not match:
if not compare_versions(version_found, op, version_required):
self._fill_missing_dep(dep_name=dep_name, dep_exp=dep, aur_index=aur_index,
missing_deps=missing_deps,
remote_provided_map=remote_provided_map,

View File

@@ -1,14 +1,11 @@
import logging
import os
import traceback
from typing import Optional
from colorama import Fore
from packaging.version import parse as parse_version
from bauh.api.abstract.model import PackageStatus
from bauh.api.http import HttpClient
from bauh.gems.arch.model import ArchPackage
from bauh.gems.arch.version import compare_versions
from bauh.view.util.translation import I18n
URL_PKG_DOWNLOAD = 'https://aur.archlinux.org/{}'
@@ -64,26 +61,7 @@ class AURDataMapper:
@staticmethod
def check_version_update(version: str, latest_version: str) -> bool:
if version and latest_version and version != latest_version:
try:
ver_epoch, latest_epoch = version.split(':'), latest_version.split(':')
if len(ver_epoch) > 1 and len(latest_epoch) > 1:
parsed_ver_epoch, parsed_latest_epoch = parse_version(ver_epoch[0]), parse_version(latest_epoch[0])
if parsed_ver_epoch == parsed_latest_epoch:
return parse_version(''.join(ver_epoch[1:])) < parse_version(''.join(latest_epoch[1:]))
else:
return parsed_ver_epoch < parsed_latest_epoch
elif len(ver_epoch) > 1 and len(latest_epoch) == 1:
return False
elif len(ver_epoch) == 1 and len(latest_epoch) > 1:
return True
else:
return parse_version(version) < parse_version(latest_version)
except:
print('{}Version: {}. Latest version: {}{}'.format(Fore.RED, version, latest_version, Fore.RESET))
traceback.print_exc()
return False
return compare_versions(latest_version, '>', version)
return False

View File

@@ -4,8 +4,6 @@ import traceback
from threading import Thread
from typing import Dict, Set, List, Tuple, Iterable, Optional
from packaging.version import parse as parse_version
from bauh.api.abstract.controller import UpgradeRequirements, UpgradeRequirement
from bauh.api.abstract.handler import ProcessWatcher
from bauh.gems.arch import pacman, sorting
@@ -14,6 +12,7 @@ from bauh.gems.arch.dependencies import DependenciesAnalyser
from bauh.gems.arch.exceptions import PackageNotFoundException
from bauh.gems.arch.model import ArchPackage
from bauh.gems.arch.pacman import RE_DEP_OPERATORS
from bauh.gems.arch.version import compare_versions
from bauh.view.util.translation import I18n
@@ -733,9 +732,7 @@ class UpdatesSummarizer:
for v in versions:
try:
provided_version, required_version = parse_version(v), parse_version(dep_split[1])
if eval('provided_version {} required_version'.format(op)):
if compare_versions(v, op, dep_split[1]):
version_match = True
break
except:

35
bauh/gems/arch/version.py Normal file
View File

@@ -0,0 +1,35 @@
from typing import Optional
from packaging.version import parse
def normalize_version(version: str) -> Optional[str]:
if version:
final_version = version.strip()
if final_version:
if ':' not in final_version:
final_version = '0:{}'.format(final_version)
if '-' not in final_version:
final_version = '{}-1'.format(final_version)
return final_version
def compare_versions(version_a: str, operator: str, version_b: str) -> bool:
if operator:
a_obj, b_obj = parse(normalize_version(version_a)), parse(normalize_version(version_b))
if operator == '>':
return a_obj > b_obj
elif operator == '>=':
return a_obj >= b_obj
elif operator == '<':
return a_obj < b_obj
elif operator == '<=':
return a_obj <= b_obj
elif operator == '==':
return a_obj == b_obj
raise Exception("compare: invalid string operator {}".format(operator))

0
tests/common/__init__.py Normal file
View File

View File

@@ -0,0 +1,46 @@
from unittest import TestCase
from bauh.gems.arch import version
class CompareVersionsTest(TestCase):
def test_both_versions_only_filled_with_numbers_and_same_number_of_divisions(self):
self.assertFalse(version.compare_versions('1', '>', '2'))
self.assertTrue(version.compare_versions('1', '<', '2'))
self.assertTrue(version.compare_versions('1', '<=', '2'))
self.assertTrue(version.compare_versions('2', '>=', '1'))
self.assertTrue(version.compare_versions('1', '==', '1'))
self.assertFalse(version.compare_versions('1.1', '>', '2.0'))
self.assertTrue(version.compare_versions('1.1', '<', '2.0'))
self.assertTrue(version.compare_versions('1.1', '<=', '2.0'))
self.assertTrue(version.compare_versions('2.1', '>', '2.0'))
self.assertTrue(version.compare_versions('2.1', '>=', '2.0'))
self.assertTrue(version.compare_versions('2.0', '==', '2.0'))
def test_versions_only_filled_with_numbers_and_different_number_of_divisons(self):
self.assertFalse(version.compare_versions('1.0', '>', '2'))
self.assertTrue(version.compare_versions('1', '<', '2.0'))
self.assertTrue(version.compare_versions('1.1.1', '<=', '2'))
self.assertTrue(version.compare_versions('2', '>=', '1.0'))
self.assertTrue(version.compare_versions('1.0', '==', '1'))
self.assertTrue(version.compare_versions('1.1', '>', '1'))
self.assertFalse(version.compare_versions('1.1', '<', '1'))
def test_one_version_contain_mixed_letters_and_symbols(self):
self.assertTrue(version.compare_versions('2.3.3op2', '>=', '2.2.6'))
self.assertTrue(version.compare_versions('2.2a', '<', '2.2b'))
self.assertTrue(version.compare_versions('2.2a', '<', '2.3a'))
def test_versions_with_epochs(self):
self.assertTrue(version.compare_versions('1:1', '==', '1:1.0'))
self.assertTrue(version.compare_versions('1:1', '<', '1:1.1'))
self.assertTrue(version.compare_versions('1:1', '<=', '1:1.1'))
self.assertTrue(version.compare_versions('2:0', '>', '1:1.1'))
self.assertTrue(version.compare_versions('1:0', '>', '2.0-1'))
def test_versions_with_release_number(self):
self.assertTrue(version.compare_versions('1.0-1', '<', '1.0-2'))
self.assertTrue(version.compare_versions('1.0-2', '>', '1.0-1'))
self.assertTrue(version.compare_versions('1.0-1', '<', '1.0-10'))
self.assertTrue(version.compare_versions('1.0-10', '>', '1.0-1'))