From c31b410465546e04b6920f395451198c9ceda82d Mon Sep 17 00:00:00 2001 From: shenxianpeng Date: Mon, 20 Oct 2025 22:44:52 +0300 Subject: [PATCH 1/4] feat: create script to update versions automaticlly --- .github/workflows/update-versions.yml | 86 +++++++++++++++++++++++++ cpp_linter_hooks/util.py | 91 +------------------------- cpp_linter_hooks/versions.py | 93 +++++++++++++++++++++++++++ scripts/update_versions.py | 87 +++++++++++++++++++++++++ tests/test_util.py | 3 +- 5 files changed, 269 insertions(+), 91 deletions(-) create mode 100644 .github/workflows/update-versions.yml create mode 100644 cpp_linter_hooks/versions.py create mode 100644 scripts/update_versions.py diff --git a/.github/workflows/update-versions.yml b/.github/workflows/update-versions.yml new file mode 100644 index 0000000..fe79983 --- /dev/null +++ b/.github/workflows/update-versions.yml @@ -0,0 +1,86 @@ +name: Update Tool Versions + +on: + schedule: + # Run every Monday at 2 AM UTC + - cron: '0 2 * * 1' + workflow_dispatch: # Allow manual trigger + +jobs: + update-versions: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Update versions + run: | + python3 scripts/update_versions.py + + - name: Check for changes + id: verify-changed-files + run: | + if [ -n "$(git status --porcelain)" ]; then + echo "changed=true" >> $GITHUB_OUTPUT + else + echo "changed=false" >> $GITHUB_OUTPUT + fi + + - name: Get latest versions for PR description + if: steps.verify-changed-files.outputs.changed == 'true' + id: get-versions + run: | + CLANG_FORMAT_VERSION=$(python3 -c 'from cpp_linter_hooks.versions import CLANG_FORMAT_VERSIONS; print(CLANG_FORMAT_VERSIONS[-1])') + CLANG_TIDY_VERSION=$(python3 -c 'from cpp_linter_hooks.versions import CLANG_TIDY_VERSIONS; print(CLANG_TIDY_VERSIONS[-1])') + echo "clang_format_version=$CLANG_FORMAT_VERSION" >> $GITHUB_OUTPUT + echo "clang_tidy_version=$CLANG_TIDY_VERSION" >> $GITHUB_OUTPUT + + - name: Create Pull Request + if: steps.verify-changed-files.outputs.changed == 'true' + uses: peter-evans/create-pull-request@v5 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: | + chore: update clang-format and clang-tidy versions + + - Updated versions automatically from PyPI + title: "chore: update tool versions to latest" + body: | + ## 🔄 Automated Version Update + + This PR updates the hardcoded versions for clang-format and clang-tidy tools to the latest available versions from PyPI. + + ### 📦 Updated Versions + - **clang-format**: `${{ steps.get-versions.outputs.clang_format_version }}` + - **clang-tidy**: `${{ steps.get-versions.outputs.clang_tidy_version }}` + + ### 🤖 Automation Details + - This update was triggered automatically by the scheduled workflow + - Versions are fetched from the official PyPI APIs + - Only stable versions (no pre-releases) are included + + ### ✅ What's Changed + - Updated `cpp_linter_hooks/versions.py` with latest tool versions + - No breaking changes expected + - Maintains backward compatibility + + ### 🔍 Review Checklist + - [ ] Verify the new versions are stable releases + - [ ] Check that no pre-release versions were included + - [ ] Confirm the version format matches expectations + + --- + + *This PR was created automatically by the `update-versions.yml` workflow.* + branch: update-tool-versions + delete-branch: true + base: main + labels: dependencies diff --git a/cpp_linter_hooks/util.py b/cpp_linter_hooks/util.py index 2149d28..37d431a 100644 --- a/cpp_linter_hooks/util.py +++ b/cpp_linter_hooks/util.py @@ -10,6 +10,8 @@ except ModuleNotFoundError: import tomli as tomllib +from cpp_linter_hooks.versions import CLANG_FORMAT_VERSIONS, CLANG_TIDY_VERSIONS + LOG = logging.getLogger(__name__) @@ -32,95 +34,6 @@ def get_version_from_dependency(tool: str) -> Optional[str]: DEFAULT_CLANG_TIDY_VERSION = get_version_from_dependency("clang-tidy") -# https://pypi.org/pypi/clang-format/json -CLANG_FORMAT_VERSIONS = [ - "6.0.1", - "7.1.0", - "8.0.1", - "9.0.0", - "10.0.1", - "10.0.1.1", - "11.0.1", - "11.0.1.1", - "11.0.1.2", - "11.1.0", - "11.1.0.1", - "11.1.0.2", - "12.0.1", - "12.0.1.1", - "12.0.1.2", - "13.0.0", - "13.0.1", - "13.0.1.1", - "14.0.0", - "14.0.1", - "14.0.3", - "14.0.4", - "14.0.5", - "14.0.6", - "15.0.4", - "15.0.6", - "15.0.7", - "16.0.0", - "16.0.1", - "16.0.2", - "16.0.3", - "16.0.4", - "16.0.5", - "16.0.6", - "17.0.1", - "17.0.2", - "17.0.3", - "17.0.4", - "17.0.5", - "17.0.6", - "18.1.0", - "18.1.1", - "18.1.2", - "18.1.3", - "18.1.4", - "18.1.5", - "18.1.6", - "18.1.7", - "18.1.8", - "19.1.0", - "19.1.1", - "19.1.2", - "19.1.3", - "19.1.4", - "19.1.5", - "19.1.6", - "19.1.7", - "20.1.0", - "20.1.3", - "20.1.4", - "20.1.5", - "20.1.6", - "20.1.7", - "20.1.8", - "21.1.0", - "21.1.1", - "21.1.2", -] - -# https://pypi.org/pypi/clang-tidy/json -CLANG_TIDY_VERSIONS = [ - "13.0.1.1", - "14.0.6", - "15.0.2", - "15.0.2.1", - "16.0.4", - "17.0.1", - "18.1.1", - "18.1.8", - "19.1.0", - "19.1.0.1", - "20.1.0", - "21.1.0", - "21.1.1", -] - - def _resolve_version(versions: List[str], user_input: Optional[str]) -> Optional[str]: """Resolve the latest matching version based on user input and available versions.""" if user_input is None: diff --git a/cpp_linter_hooks/versions.py b/cpp_linter_hooks/versions.py new file mode 100644 index 0000000..24ce94e --- /dev/null +++ b/cpp_linter_hooks/versions.py @@ -0,0 +1,93 @@ +""" +Version management for clang-format and clang-tidy. + +This module provides hardcoded version lists that are updated periodically via GitHub Actions. +""" + +# Updated automatically by GitHub Actions - DO NOT EDIT MANUALLY +CLANG_FORMAT_VERSIONS = [ + "6.0.1", + "7.1.0", + "8.0.1", + "9.0.0", + "10.0.1", + "10.0.1.1", + "11.0.1", + "11.0.1.1", + "11.0.1.2", + "11.1.0", + "11.1.0.1", + "11.1.0.2", + "12.0.1", + "12.0.1.1", + "12.0.1.2", + "13.0.0", + "13.0.1", + "13.0.1.1", + "14.0.0", + "14.0.1", + "14.0.3", + "14.0.4", + "14.0.5", + "14.0.6", + "15.0.4", + "15.0.6", + "15.0.7", + "16.0.0", + "16.0.1", + "16.0.2", + "16.0.3", + "16.0.4", + "16.0.5", + "16.0.6", + "17.0.1", + "17.0.2", + "17.0.3", + "17.0.4", + "17.0.5", + "17.0.6", + "18.1.0", + "18.1.1", + "18.1.2", + "18.1.3", + "18.1.4", + "18.1.5", + "18.1.6", + "18.1.7", + "18.1.8", + "19.1.0", + "19.1.1", + "19.1.2", + "19.1.3", + "19.1.4", + "19.1.5", + "19.1.6", + "19.1.7", + "20.1.0", + "20.1.3", + "20.1.4", + "20.1.5", + "20.1.6", + "20.1.7", + "20.1.8", + "21.1.0", + "21.1.1", + "21.1.2", +] + +# Updated automatically by GitHub Actions - DO NOT EDIT MANUALLY +CLANG_TIDY_VERSIONS = [ + "13.0.1.1", + "14.0.6", + "15.0.2", + "15.0.2.1", + "16.0.4", + "17.0.1", + "18.1.1", + "18.1.8", + "19.1.0", + "19.1.0.1", + "20.1.0", + "21.1.0", + "21.1.1", +] diff --git a/scripts/update_versions.py b/scripts/update_versions.py new file mode 100644 index 0000000..041447b --- /dev/null +++ b/scripts/update_versions.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +""" +Script to update clang-format and clang-tidy versions from PyPI API. + +Usage: + python scripts/update_versions.py +""" + +import json +import urllib.request +from pathlib import Path +import re +from typing import List + + +def fetch_versions_from_pypi(package_name: str) -> List[str]: + """Fetch available versions for a package from PyPI API.""" + url = f"https://pypi.org/pypi/{package_name}/json" + try: + with urllib.request.urlopen(url, timeout=10) as response: + data = json.loads(response.read()) + versions = list(data["releases"].keys()) + # Filter out pre-release versions and sort + stable_versions = [ + v + for v in versions + if not any(char in v for char in ["a", "b", "rc", "dev"]) + ] + return sorted(stable_versions, key=lambda x: tuple(map(int, x.split(".")))) + except Exception as e: + print(f"Failed to fetch versions for {package_name}: {e}") + return [] + + +def update_versions_file(): + """Update the versions.py file with latest versions from PyPI.""" + clang_format_versions = fetch_versions_from_pypi("clang-format") + clang_tidy_versions = fetch_versions_from_pypi("clang-tidy") + + if not clang_format_versions or not clang_tidy_versions: + print("Failed to fetch versions from PyPI") + return False + + versions_file = Path(__file__).parent.parent / "cpp_linter_hooks" / "versions.py" + + with open(versions_file, "r") as f: + content = f.read() + + # Update clang-format versions + clang_format_list = ( + "[\n" + "\n".join(f' "{v}",' for v in clang_format_versions) + "\n]" + ) + content = re.sub( + r"(CLANG_FORMAT_VERSIONS = )\[.*?\]", + rf"\1{clang_format_list}", + content, + flags=re.DOTALL, + ) + + # Update clang-tidy versions + clang_tidy_list = ( + "[\n" + "\n".join(f' "{v}",' for v in clang_tidy_versions) + "\n]" + ) + content = re.sub( + r"(CLANG_TIDY_VERSIONS = )\[.*?\]", + rf"\1{clang_tidy_list}", + content, + flags=re.DOTALL, + ) + + with open(versions_file, "w") as f: + f.write(content) + + print("Updated versions:") + print( + f" clang-format: {len(clang_format_versions)} versions (latest: {clang_format_versions[-1]})" + ) + print( + f" clang-tidy: {len(clang_tidy_versions)} versions (latest: {clang_tidy_versions[-1]})" + ) + + return True + + +if __name__ == "__main__": + success = update_versions_file() + exit(0 if success else 1) diff --git a/tests/test_util.py b/tests/test_util.py index 9ce5b21..38d5e6d 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -9,11 +9,10 @@ _resolve_version, _install_tool, _resolve_install, - CLANG_FORMAT_VERSIONS, - CLANG_TIDY_VERSIONS, DEFAULT_CLANG_FORMAT_VERSION, DEFAULT_CLANG_TIDY_VERSION, ) +from cpp_linter_hooks.versions import CLANG_FORMAT_VERSIONS, CLANG_TIDY_VERSIONS VERSIONS = [None, "20"] From 518be044bdfa27c5740ec047774ac3ec2b8f72aa Mon Sep 17 00:00:00 2001 From: shenxianpeng Date: Mon, 20 Oct 2025 22:48:59 +0300 Subject: [PATCH 2/4] chore: use full commit SHA hash for this dependency --- .github/workflows/update-versions.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-versions.yml b/.github/workflows/update-versions.yml index fe79983..7b29353 100644 --- a/.github/workflows/update-versions.yml +++ b/.github/workflows/update-versions.yml @@ -45,7 +45,7 @@ jobs: - name: Create Pull Request if: steps.verify-changed-files.outputs.changed == 'true' - uses: peter-evans/create-pull-request@v5 + uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 with: token: ${{ secrets.GITHUB_TOKEN }} commit-message: | From 07ec87c856cf46e556f9fb6189ce4b8c6471afab Mon Sep 17 00:00:00 2001 From: shenxianpeng Date: Mon, 20 Oct 2025 22:58:08 +0300 Subject: [PATCH 3/4] fix: update update_versions.py --- scripts/update_versions.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/update_versions.py b/scripts/update_versions.py index 041447b..2b1ac2d 100644 --- a/scripts/update_versions.py +++ b/scripts/update_versions.py @@ -20,11 +20,12 @@ def fetch_versions_from_pypi(package_name: str) -> List[str]: with urllib.request.urlopen(url, timeout=10) as response: data = json.loads(response.read()) versions = list(data["releases"].keys()) - # Filter out pre-release versions and sort + # Filter out pre-release versions using proper regex patterns + pre_release_pattern = re.compile(r".*(alpha|beta|rc|dev|a\d+|b\d+).*", re.IGNORECASE) stable_versions = [ v for v in versions - if not any(char in v for char in ["a", "b", "rc", "dev"]) + if not pre_release_pattern.match(v) ] return sorted(stable_versions, key=lambda x: tuple(map(int, x.split(".")))) except Exception as e: @@ -51,7 +52,7 @@ def update_versions_file(): "[\n" + "\n".join(f' "{v}",' for v in clang_format_versions) + "\n]" ) content = re.sub( - r"(CLANG_FORMAT_VERSIONS = )\[.*?\]", + r"(CLANG_FORMAT_VERSIONS = )\[[^\]]*\]", rf"\1{clang_format_list}", content, flags=re.DOTALL, @@ -62,7 +63,7 @@ def update_versions_file(): "[\n" + "\n".join(f' "{v}",' for v in clang_tidy_versions) + "\n]" ) content = re.sub( - r"(CLANG_TIDY_VERSIONS = )\[.*?\]", + r"(CLANG_TIDY_VERSIONS = )\[[^\]]*\]", rf"\1{clang_tidy_list}", content, flags=re.DOTALL, From 75397a4ba74876bb93ed4861dfc8ccf6889fdd80 Mon Sep 17 00:00:00 2001 From: shenxianpeng Date: Mon, 20 Oct 2025 22:59:09 +0300 Subject: [PATCH 4/4] fix: update update_versions.py --- scripts/update_versions.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/scripts/update_versions.py b/scripts/update_versions.py index 2b1ac2d..4afc3af 100644 --- a/scripts/update_versions.py +++ b/scripts/update_versions.py @@ -21,12 +21,10 @@ def fetch_versions_from_pypi(package_name: str) -> List[str]: data = json.loads(response.read()) versions = list(data["releases"].keys()) # Filter out pre-release versions using proper regex patterns - pre_release_pattern = re.compile(r".*(alpha|beta|rc|dev|a\d+|b\d+).*", re.IGNORECASE) - stable_versions = [ - v - for v in versions - if not pre_release_pattern.match(v) - ] + pre_release_pattern = re.compile( + r".*(alpha|beta|rc|dev|a\d+|b\d+).*", re.IGNORECASE + ) + stable_versions = [v for v in versions if not pre_release_pattern.match(v)] return sorted(stable_versions, key=lambda x: tuple(map(int, x.split(".")))) except Exception as e: print(f"Failed to fetch versions for {package_name}: {e}")