apt: handle multiple verions correctly

closes #191
This commit is contained in:
lilydjwg 2021-06-25 15:22:40 +08:00
parent 4c4b770c27
commit 6fd3ba95ba
2 changed files with 79 additions and 6 deletions

View File

@ -3,9 +3,12 @@
from __future__ import annotations from __future__ import annotations
import re
import asyncio import asyncio
from io import StringIO
from typing import Dict, Tuple from typing import Dict, Tuple
import itertools
import functools
from collections import defaultdict
from nvchecker.api import ( from nvchecker.api import (
session, GetVersionError, session, GetVersionError,
@ -17,6 +20,60 @@ APT_PACKAGES_PATH = "%s/binary-%s/Packages%s"
APT_PACKAGES_URL = "%s/dists/%s/%s" APT_PACKAGES_URL = "%s/dists/%s/%s"
APT_PACKAGES_SUFFIX_PREFER = (".xz", ".gz", "") APT_PACKAGES_SUFFIX_PREFER = (".xz", ".gz", "")
DpkgVersion = Tuple[int, str, str]
def parse_version(s: str) -> DpkgVersion:
try:
epoch_str, rest = s.split(':', 1)
except ValueError:
epoch = 0
rest = s
else:
epoch = int(epoch_str)
try:
ver, rev = rest.split('-', 1)
except ValueError:
ver = rest
rev = ''
return epoch, ver, rev
def _compare_part(a: str, b: str) -> int:
sa = re.split(r'(\d+)', a)
sb = re.split(r'(\d+)', b)
for idx, (pa, pb) in enumerate(itertools.zip_longest(sa, sb)):
if pa is None:
return -1
elif pb is None:
return 1
if idx % 2 == 1:
ret = int(pa) - int(pb)
if ret != 0:
return ret
else:
if pa < pb:
return -1
elif pa > pb:
return 1
return 0
def compare_version_parsed(a: DpkgVersion, b: DpkgVersion) -> int:
ret = a[0] - b[0]
if ret != 0:
return ret
ret = _compare_part(a[1], b[1])
if ret != 0:
return ret
return _compare_part(a[2], b[2])
def compare_version(a: str, b: str) -> int:
va = parse_version(a)
vb = parse_version(b)
return compare_version_parsed(va, vb)
def _decompress_data(url: str, data: bytes) -> str: def _decompress_data(url: str, data: bytes) -> str:
if url.endswith(".xz"): if url.endswith(".xz"):
import lzma import lzma
@ -39,8 +96,8 @@ async def parse_packages(key: Tuple[AsyncCache, str]) -> Tuple[Dict[str, str], D
cache, url = key cache, url = key
apt_packages = await cache.get(url, get_url) # type: ignore apt_packages = await cache.get(url, get_url) # type: ignore
pkg_map = {} pkg_map = defaultdict(list)
srcpkg_map = {} srcpkg_map = defaultdict(list)
pkg = None pkg = None
srcpkg = None srcpkg = None
@ -52,12 +109,17 @@ async def parse_packages(key: Tuple[AsyncCache, str]) -> Tuple[Dict[str, str], D
elif line.startswith("Version: "): elif line.startswith("Version: "):
version = line[9:] version = line[9:]
if pkg is not None: if pkg is not None:
pkg_map[pkg] = version pkg_map[pkg].append(version)
if srcpkg is not None: if srcpkg is not None:
srcpkg_map[srcpkg] = version srcpkg_map[srcpkg].append(version)
pkg = srcpkg = None pkg = srcpkg = None
return pkg_map, srcpkg_map pkg_map_max = {pkg: max(vs, key=functools.cmp_to_key(compare_version))
for pkg, vs in pkg_map.items()}
srcpkg_map_max = {pkg: max(vs, key=functools.cmp_to_key(compare_version))
for pkg, vs in srcpkg_map.items()}
return pkg_map_max, srcpkg_map_max
async def get_version( async def get_version(
name: str, conf: Entry, *, name: str, conf: Entry, *,

View File

@ -39,3 +39,14 @@ async def test_apt_deepin(get_version):
"mirror": "https://community-packages.deepin.com/deepin", "mirror": "https://community-packages.deepin.com/deepin",
"suite": "apricot", "suite": "apricot",
}) == "0.1.6-1" }) == "0.1.6-1"
@flaky(max_runs=10)
async def test_apt_multiversions(get_version):
assert await get_version("ms-teams", {
"source": "apt",
"mirror": "https://packages.microsoft.com/repos/ms-teams",
"pkg": "teams",
"suite": "stable",
"repo": "main",
"arch": "amd64",
}) == "1.4.00.13653"