Last active
July 31, 2024 12:54
-
-
Save valgur/c1c200e93dc64fe937bc36a9c23cc7c5 to your computer and use it in GitHub Desktop.
Update the dependencies of a ConanCenterIndex recipe
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# suppress distutils warning | |
import warnings | |
warnings.filterwarnings("ignore", category=DeprecationWarning) | |
import datetime | |
import re | |
import subprocess | |
import sys | |
from pathlib import Path | |
from distutils.version import LooseVersion | |
from conan.tools.scm import Version | |
import yaml | |
repo_root = Path(__file__).parent.absolute() | |
recipes_dir = repo_root / "recipes" | |
cache: dict = {} | |
ranges = { | |
"openssl": "[>=1.1 <4]", | |
"curl": "[>=7.78 <9]", | |
"libcurl": "[>=7.78 <9]", | |
"zlib": "[>=1.2.11 <2]", | |
"libpng": "[>=1.6 <2]", | |
"expat": "[>=2.6.2 <3]", | |
"libxml2": "[>=2.12.5 <3]", | |
"libuv": "[>=1 <2]", | |
"qt": "[>=6.7 <7]", | |
"c-ares": "[>=1.27 <2]", | |
"zstd": "[^1.5]", | |
"ninja": "[>=1.10.2 <2]", | |
"meson": "[>=1.2.3 <2]", | |
"pkgconf": "[>=2.2 <3]", | |
"xz_utils": "[>=5.4.5 <6]", | |
# overrides | |
"libjpeg": "9e", | |
} | |
def _get_config_versions(config_path): | |
with open(config_path, "r") as f: | |
config = yaml.safe_load(f) | |
return list(config["versions"]) | |
def find_latest_version(config_path): | |
pkg = config_path.parent.name | |
# if pkg in cache: | |
# return cache[pkg] | |
config_versions = _get_config_versions(config_path) | |
# If all versions are in a standard format, sort by version only | |
if all(re.fullmatch(r"[\d.]+", v) for v in config_versions): | |
return sorted([(LooseVersion(v), v) for v in config_versions], reverse=True)[0][1] | |
# Otherwise, sort by commit date, then version | |
versions = [] | |
blame = subprocess.run(["git", "blame", "-e", config_path], capture_output=True, text=True).stdout | |
for line in blame.splitlines(): | |
line = line.split("#")[0].strip() | |
if "versions" in line or "folder" in line or line[-1] != ":": | |
continue | |
# 815109b9a45 (SpaceIm 2022-11-08 20:49:32 +0100 26) "1.3.5": | |
m = re.match(r'^\w+ (?:\S+ )?\(.+\s+(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} [+-]\d{4}) +\d+\) +"?([^"]+)"?:', line) | |
if not m: | |
print(" Matching failed:", line, file=sys.stderr) | |
continue | |
date = datetime.datetime.strptime(m.group(1), "%Y-%m-%d %H:%M:%S %z").timestamp() | |
version = m.group(2) | |
version_sort = LooseVersion(version.replace("cci.", "")) | |
versions.append((date, version_sort, version)) | |
return sorted(versions, reverse=True)[0][-1] | |
def main(conanfile_path): | |
# _init_cache() | |
content = Path(conanfile_path).read_text() | |
deps = re.findall(r'"([a-z0-9_.-]+)/([a-z0-9_.-\[\]<>= ~^]+)"', content) | |
for dep, current_version in deps: | |
if dep in ["lib", "include", "bin", "src", "share", "etc", "doc", "res", "cmake", ".", ".."]: | |
# probably matched a path | |
continue | |
if len(parts := current_version.rsplit(".")) >= 2: | |
if len(parts[0]) >= 4 and len(parts[-1]) <= 3 and parts[-1].isalpha(): | |
# probably matched a path | |
continue | |
print(f" Processing {dep}") | |
if current_version in ["system", "cci.latest"]: | |
continue | |
config_path = recipes_dir / dep / "config.yml" | |
if not config_path.exists(): | |
print(f" ERROR: {config_path} does not exist", file=sys.stderr) | |
continue | |
if dep == "cmake": | |
v = Version(current_version) | |
latest = f"[>={v.major}.{v.minor}]" | |
elif dep in ranges: | |
latest = ranges[dep] | |
else: | |
latest = find_latest_version(config_path) | |
if latest != current_version: | |
content = content.replace(f'"{dep}/{current_version}"', f'"{dep}/{latest}"') | |
Path(conanfile_path).write_text(content) | |
def _init_cache(): | |
global cache | |
cache_path = repo_root / ".versions_cache.yml" | |
if cache_path.exists(): | |
with cache_path.open() as f: | |
cache = yaml.safe_load(f) | |
return | |
print(" Initializing versions cache...") | |
for recipe in sorted(recipes_dir.iterdir()): | |
if recipe.is_dir(): | |
try: | |
print(f" Processing {recipe.name}") | |
cache[recipe.name] = find_latest_version(recipe / "config.yml") | |
except Exception as e: | |
print(f" ERROR: {recipe.name}: {e}", file=sys.stderr) | |
with cache_path.open("w") as f: | |
yaml.safe_dump(cache, f) | |
if __name__ == "__main__": | |
if len(sys.argv) < 2: | |
_init_cache() | |
else: | |
main(sys.argv[1]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment