Skip to content

Instantly share code, notes, and snippets.

@valgur
Last active July 31, 2024 12:54
Show Gist options
  • Save valgur/c1c200e93dc64fe937bc36a9c23cc7c5 to your computer and use it in GitHub Desktop.
Save valgur/c1c200e93dc64fe937bc36a9c23cc7c5 to your computer and use it in GitHub Desktop.
Update the dependencies of a ConanCenterIndex recipe
#!/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