From 3a4adfac529b2124b90e4feb38289d37a400f878 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 11 May 2026 07:02:17 +0200 Subject: [PATCH] feat: auto-update pre-commit config --- build_helpers/pre_commit_update.py | 44 ++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/build_helpers/pre_commit_update.py b/build_helpers/pre_commit_update.py index 9e49aa7c2..9b964c6b4 100644 --- a/build_helpers/pre_commit_update.py +++ b/build_helpers/pre_commit_update.py @@ -1,5 +1,7 @@ # File used in CI to ensure pre-commit dependencies are kept up-to-date. +import argparse +import re import sys from pathlib import Path @@ -10,6 +12,24 @@ pre_commit_file = Path(".pre-commit-config.yaml") require_dev = Path("requirements-dev.txt") require = Path("requirements.txt") + +parser = argparse.ArgumentParser() +parser.add_argument("--update", action="store_true") +args = parser.parse_args() + + +def replace_dependency_version(pre_commit_text: str, dependency: str) -> tuple[str, bool]: + """ + Regex-based replacement of a dependency version in the pre-commit config file. + using regex here ensures we only replace the version of the dependency while + keeping the overall file intact. + """ + package_name = dependency.split("==", 1)[0] + pattern = re.compile(rf"^(\s*-\s+){re.escape(package_name)}==.*$", re.MULTILINE) + updated_text, replacements = pattern.subn(rf"\1{dependency}", pre_commit_text, count=1) + return updated_text, replacements > 0 and updated_text != pre_commit_text + + with require_dev.open("r") as rfile: requirements = rfile.readlines() @@ -23,6 +43,18 @@ supported = ("types-", "SQLAlchemy", "scipy-stubs") # Only keep the first part of the line up to the first space type_reqs = [r.strip("\n").split()[0] for r in requirements if r.startswith(supported)] +with pre_commit_file.open("r") as file: + pre_commit_text = file.read() + +updated = False +for req in type_reqs: + pre_commit_text, req_updated = replace_dependency_version(pre_commit_text, req) + updated = updated or req_updated + +if args.update and updated: + with pre_commit_file.open("w") as file: + file.write(pre_commit_text) + with pre_commit_file.open("r") as file: f = yaml.load(file, Loader=yaml.SafeLoader) @@ -40,12 +72,20 @@ for hook in hooks: for req in type_reqs: if req not in hooks: - errors.append(f"{req} is missing in pre-config file.") + errors.append(f"{req} is missing in pre-commit config file.") + +if updated: + if args.update: + errors.append(".pre-commit-config.yaml was updated to match the requirements files.") + else: + errors.append( + ".pre-commit-config.yaml is outdated. Run build_helpers/pre_commit_update.py --update." + ) if errors: for e in errors: print(e) - sys.exit(1) + sys.exit(1 if not (args.update and updated) else 0) sys.exit(0)