diff --git a/INSTALL.md b/INSTALL.md index ea6ee2e23..9ff0055ad 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -127,7 +127,7 @@ Alternatively, follow the Linux installation instructions above. You might need to also do: ```bash -brew install uv python-tk@3.9 +brew install uv@0.10.9 python-tk@3.9 ``` ## Install *Mission Planner* software on a PC or Mac @@ -218,10 +218,10 @@ This method uses the official SLSA verifier to check the cryptographic provenanc ```bash # Download the latest SLSA verifier (recommended: check https://github.com/slsa-framework/slsa-verifier/releases for the latest version) -# Replace with the latest release tag, e.g. v2.7.0 -curl -sSLO https://github.com/slsa-framework/slsa-verifier/releases/latest/download/slsa-verifier-linux-amd64 +# Replace with the latest release tag, e.g. v2.7.1 +curl -sSLO https://github.com/slsa-framework/slsa-verifier/releases/download/v2.7.1/slsa-verifier-linux-amd64 chmod +x slsa-verifier-linux-amd64 -# Alternatively, to use a specific version, replace 'latest' with the desired version tag (e.g. v2.7.0), but ensure you check for updates regularly. +# Alternatively, to use a specific version, replace the version tag (e.g. v2.7.1) with the desired version, but ensure you check for updates regularly. # Verify the installer ./slsa-verifier-linux-amd64 verify-artifact \ @@ -237,7 +237,7 @@ chmod +x slsa-verifier-linux-amd64 ```powershell # Download the SLSA verifier -Invoke-WebRequest -Uri "https://github.com/slsa-framework/slsa-verifier/releases/download/v2.7.0/slsa-verifier-windows-amd64.exe" -OutFile "slsa-verifier.exe" +Invoke-WebRequest -Uri "https://github.com/slsa-framework/slsa-verifier/releases/download/v2.7.1/slsa-verifier-windows-amd64.exe" -OutFile "slsa-verifier.exe" # Verify the installer .\slsa-verifier.exe verify-artifact ` @@ -257,7 +257,7 @@ If Cosign signatures are available, you can also verify using Cosign: ```bash # Install Cosign (if not already installed) -curl -O -L "https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64" +curl -O -L "https://github.com/sigstore/cosign/releases/download/v3.0.6/cosign-linux-amd64" chmod +x cosign-linux-amd64 sudo mv cosign-linux-amd64 /usr/local/bin/cosign diff --git a/SetupDeveloperPC.sh b/SetupDeveloperPC.sh index 9c902c623..183d9abff 100755 --- a/SetupDeveloperPC.sh +++ b/SetupDeveloperPC.sh @@ -35,7 +35,7 @@ if command -v brew &> /dev/null; then fi echo "Found Python $PY_VER, installing python-tk..." - brew install uv python-tk@"$PY_VER" + brew install uv@0.10.9 python-tk@"$PY_VER" echo "Creating Python virtual environment with uv..." uv venv --python "$PY_VER" diff --git a/renovate.json b/renovate.json index d041a13c8..60dda5b63 100644 --- a/renovate.json +++ b/renovate.json @@ -18,7 +18,10 @@ "windows/*.bat", "install_*.sh", "install_*.bash", - "install_*.bat" + "install_*.bat", + "INSTALL.md", + ".github/workflows/*.yml", + ".github/workflows/*.yaml" ], "packageRules": [ { @@ -58,50 +61,46 @@ "customType": "regex", "managerFilePatterns": ["/\\.bash$/", "/\\.sh$/"], "matchStrings": [ - "^npm install --global (?[^@\\s]+)@(?[\\d\\.]+)(?:\\s+--integrity\\s+(?sha[0-9]+-[A-Za-z0-9+/=]+))?\\s*$" + "npm install --global (?[^@\\s]+)@(?[\\d\\.]+)(?:\\s+--integrity\\s+(?sha[0-9]+-[A-Za-z0-9+/=]+))?" ], "datasourceTemplate": "npm", "autoReplaceStringTemplate": "npm install --global {{{depName}}}@{{{newValue}}}{{#if newDigest}} --integrity {{{newDigest}}}{{/if}}" }, { "customType": "regex", - "managerFilePatterns": ["/\\.sh$/", "/\\.bash$/"], + "managerFilePatterns": ["/\\.sh$", "/\\.bash$", "/\\.md$", "/\\.ya?ml$"], "matchStrings": [ - "brew install (?python-tk)@(?[\\d\\.]+)" + "brew (?:list python-tk &>/dev/null \\|\\| brew )?install (?:uv(?:@[\\d\\.]+)? )?(?python-tk)(?:@(?[\\w\\.\"$]+))?" ], "datasourceTemplate": "github-releases", "depNameTemplate": "python/cpython" }, { "customType": "regex", - "managerFilePatterns": ["/\\.sh$/", "/\\.bash$/"], - "matchStrings": ["brew install (?uv)(?:\\s|$)"], - "datasourceTemplate": "github-releases", - "depNameTemplate": "astral-sh/uv", - "currentValueTemplate": "latest" - }, - { - "customType": "regex", - "managerFilePatterns": ["/\\.py$/"], + "managerFilePatterns": ["/\\.sh$", "/\\.bash$", "/\\.md$"], "matchStrings": [ - "# dependencies = \\[\\s*(?:[^\\]]*\\n)*?#\\s*\"(?[^\"]+)==(?[\\d\\.]+)\"" + "brew install (?uv)(?:@(?[\\d\\.]+))?(?:\\s| python-tk|$)" ], - "datasourceTemplate": "pypi" + "datasourceTemplate": "github-releases", + "depNameTemplate": "astral-sh/uv" }, { "customType": "regex", "managerFilePatterns": ["/\\.py$/"], + "matchStringsStrategy": "recursive", "matchStrings": [ - "subprocess\\.check_call\\(\\[sys\\.executable, \"-m\", \"pip\", \"install\", \"(?[^\"]+)\"\\]\\)" + "# dependencies = \\[\\s*(?:[^\\]]*\\n)*?[^\\]]*\\]", + "\"(?[^\"=]+)(?:==(?[^\"]+))?\"" ], - "datasourceTemplate": "pypi", - "currentValueTemplate": "latest" + "datasourceTemplate": "pypi" }, { "customType": "regex", "managerFilePatterns": ["/\\.sh$/", "/\\.bash$/"], + "matchStringsStrategy": "recursive", "matchStrings": [ - "REQUIRED_PKGS=\\([^)]*\"(?[^\"@]+)(?:@(?[\\d\\.]+))?\"[^)]*\\)" + "REQUIRED_PKGS=\\([^)]+\\)", + "\"(?[^\"@]+)(?:@(?[\\d\\.]+))?\"" ], "datasourceTemplate": "pypi", "currentValueTemplate": "latest" @@ -109,28 +108,32 @@ { "customType": "regex", "managerFilePatterns": ["/\\.sh$/", "/\\.bash$/"], - "matchStrings": ["pip install (?[\\w-]+)(?:\\s|$)"], + "matchStrings": ["pip install (?[A-Za-z0-9][A-Za-z0-9._-]*)(?:\\s|$)"], "datasourceTemplate": "pypi", "currentValueTemplate": "latest" }, { "customType": "regex", - "managerFilePatterns": ["/\\.sh$/", "/\\.bash$/"], - "matchStrings": ["uv pip install (?[\\w-]+)(?:\\s|$)"], + "managerFilePatterns": ["/\\.sh$/", "/\\.bash$/", "/\\.md$", "/\\.ya?ml$"], + "matchStrings": ["uv pip install (?:\"|')?(?[A-Za-z0-9][A-Za-z0-9._-]*)(?:==(?[A-Za-z0-9._-]+))?(?:\"|')?(?:\\s|$)"], "datasourceTemplate": "pypi", "currentValueTemplate": "latest" }, { "customType": "regex", "managerFilePatterns": ["/\\.sh$/", "/\\.bash$/"], - "matchStrings": ["uv pip install[^\"\\n]*\"(?[^\"=]+)==(?[^\"]+)\""], + "matchStringsStrategy": "recursive", + "matchStrings": [ + "uv pip install(?(?:\\s+\"[^\"]+\")+)", + "\"(?[^\"=]+)==(?[^\"]+)\"" + ], "datasourceTemplate": "pypi" }, { "customType": "regex", "managerFilePatterns": ["/\\.bat$/"], "matchStrings": [ - "set \"URL=https://github\\.com/(?[^/]+/[^/]+)/releases/download/v(?[\\d\\.\\-]+)/[^\"]+\"" + "set \"URL=https://github\\.com/(?[^/]+/[^/]+)/releases/download/v(?[\\w\\.\\-]+)/[^\"]+\"" ], "datasourceTemplate": "github-releases" }, @@ -146,15 +149,18 @@ { "customType": "regex", "managerFilePatterns": ["/\\.py$/"], - "matchStrings": ["required_packages = \\[[^\\]]*\"(?[^\"=]+)==(?[^\"]+)\""], - "datasourceTemplate": "pypi", - "currentValueTemplate": "latest" + "matchStringsStrategy": "recursive", + "matchStrings": [ + "required_packages\\s*=\\s*\\[[^\\]]*\\]", + "\"(?[^\"=\\n]+)==(?[^\"\\n]+)\"" + ], + "datasourceTemplate": "pypi" }, { "customType": "regex", - "managerFilePatterns": ["/\\.bat$/"], + "managerFilePatterns": ["/\\.bat$", "/\\.md$", "/\\.ya?ml$"], "matchStrings": [ - "github\\.com/(?[^/]+/[^/]+)/releases/download/(?v[\\d\\.\\-]+)/" + "github\\.com/(?[^/]+/[^/]+)/releases/download/(?[^/]+)/" ], "datasourceTemplate": "github-releases" } diff --git a/scripts/test_renovate.py b/scripts/test_renovate.py new file mode 100755 index 000000000..dbdc87162 --- /dev/null +++ b/scripts/test_renovate.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +# ruff: noqa: T201 +""" +Test the Renovate regexes defined in renovate.json. + +Usage: + python scripts/test_renovate.py + +SPDX-FileCopyrightText: 2024-2026 Amilcar do Carmo Lucas + +SPDX-License-Identifier: GPL-3.0-or-later +""" + +import json +import os +import re +from typing import Any + + +def check_recursive(content: str, match_strings: list[str], level: int = 0) -> list[dict[str, Any]]: + if level >= len(match_strings): + return [] + + rx_str = match_strings[level].replace("?<", "?P<") + try: + rx = re.compile(rx_str) + except re.error as e: + print(f" [Error] Invalid regex {rx_str}: {e}") + return [] + + all_results = [] + + for m in rx.finditer(content): + # Base case: if we are at the last regex, we just collect its groupdict + if level == len(match_strings) - 1: + all_results.append(m.groupdict()) + else: + # Recursive case: process the matched string piece + block = m.group(0) + sub_results = check_recursive(block, match_strings, level + 1) + all_results.extend(sub_results) + + return all_results + + +def process_manager(project_root: str, manager: dict[str, Any], idx: int) -> None: + if manager.get("customType") != "regex": + return + + file_patterns = manager.get("managerFilePatterns", []) + match_strings = manager.get("matchStrings", []) + strategy = manager.get("matchStringsStrategy", "any") + print(f"\n{'=' * 50}\nManager {idx + 1}\nStrategy: {strategy}\nMatchStrings: {match_strings}\n{'-' * 50}") + + py_file_patterns = [re.compile(p.strip("/")) for p in file_patterns] + matched_any = False + + for root, _dirs, files in os.walk(project_root): + if "\\.git\\" in root or "/.git/" in root or root.endswith(".git") or ".venv" in root or "node_modules" in root: + continue + for file in files: + file_path = os.path.join(root, file) + rel_path = os.path.relpath(file_path, project_root).replace("\\", "/") + if not any(p.search(rel_path) for p in py_file_patterns): + continue + matched_any |= check_file(file_path, rel_path, strategy, match_strings) + + if not matched_any: + print(">>> NO MATCHES FOUND IN WORKSPACE <<<") + + +def check_file(file_path: str, rel_path: str, strategy: str, match_strings: list[str]) -> bool: + try: + with open(file_path, encoding="utf-8") as f: + content = f.read() + except (OSError, UnicodeDecodeError): + return False + + matched_any = False + if strategy == "recursive": + results = check_recursive(content, match_strings, 0) + if results: + matched_any = True + print(f"File: {rel_path}") + for res in results: + print(f" -> {res}") + else: + for rx_str in match_strings: + rx_str_python = rx_str.replace("?<", "?P<") + try: + rx = re.compile(rx_str_python) + matches = list(rx.finditer(content)) + if matches: + matched_any = True + print(f"File: {rel_path}") + for m in matches: + print(f" -> {m.groupdict()}") + except re.error as e: + print(f" [Error] for {rx_str}: {e}") + return matched_any + + +def test_renovate_regexes() -> None: + project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + renovate_json_path = os.path.join(project_root, "renovate.json") + + with open(renovate_json_path, encoding="utf-8") as f: + config = json.load(f) + + managers = config.get("customManagers", []) + for idx, manager in enumerate(managers): + process_manager(project_root, manager, idx) + + +if __name__ == "__main__": + test_renovate_regexes()