diff --git a/.github/workflows/update-versions.yml b/.github/workflows/update-versions.yml new file mode 100644 index 0000000..7b29353 --- /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@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 + 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..4afc3af --- /dev/null +++ b/scripts/update_versions.py @@ -0,0 +1,86 @@ +#!/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 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)] + 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"]