pre-commit-localupdate 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pre_commit_localupdate-0.2.0 → pre_commit_localupdate-0.3.0}/PKG-INFO +3 -3
- {pre_commit_localupdate-0.2.0 → pre_commit_localupdate-0.3.0}/README.md +1 -1
- pre_commit_localupdate-0.3.0/pre_commit_localupdate/__init__.py +1 -0
- {pre_commit_localupdate-0.2.0 → pre_commit_localupdate-0.3.0}/pre_commit_localupdate/cli.py +2 -1
- {pre_commit_localupdate-0.2.0 → pre_commit_localupdate-0.3.0}/pre_commit_localupdate/io.py +10 -9
- pre_commit_localupdate-0.3.0/pre_commit_localupdate/packages/__init__.py +15 -0
- {pre_commit_localupdate-0.2.0/pre_commit_localupdate → pre_commit_localupdate-0.3.0/pre_commit_localupdate/packages}/julia.py +2 -1
- {pre_commit_localupdate-0.2.0/pre_commit_localupdate → pre_commit_localupdate-0.3.0/pre_commit_localupdate/packages}/node.py +1 -1
- {pre_commit_localupdate-0.2.0/pre_commit_localupdate → pre_commit_localupdate-0.3.0/pre_commit_localupdate/packages}/python.py +1 -1
- pre_commit_localupdate-0.3.0/pre_commit_localupdate/packages/rust.py +103 -0
- {pre_commit_localupdate-0.2.0 → pre_commit_localupdate-0.3.0}/pre_commit_localupdate/pre_commit_config.py +10 -21
- {pre_commit_localupdate-0.2.0 → pre_commit_localupdate-0.3.0}/pyproject.toml +2 -2
- pre_commit_localupdate-0.2.0/pre_commit_localupdate/__init__.py +0 -1
- {pre_commit_localupdate-0.2.0 → pre_commit_localupdate-0.3.0}/LICENSE +0 -0
- {pre_commit_localupdate-0.2.0 → pre_commit_localupdate-0.3.0}/pre_commit_localupdate/__main__.py +0 -0
- {pre_commit_localupdate-0.2.0 → pre_commit_localupdate-0.3.0}/pre_commit_localupdate/logs.py +0 -0
- {pre_commit_localupdate-0.2.0/pre_commit_localupdate → pre_commit_localupdate-0.3.0/pre_commit_localupdate/packages}/package.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pre-commit-localupdate
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: A CLI tool to automatically update additional dependencies within local Python and Node.js hooks in pre-commit config files.
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: A CLI tool to automatically update additional dependencies within local Python, Julia, Rust, and Node.js hooks in pre-commit config files.
|
|
5
5
|
Author: M. Farzalipour Tabriz
|
|
6
6
|
Classifier: Development Status :: 3 - Alpha
|
|
7
7
|
Classifier: Environment :: Console
|
|
@@ -15,7 +15,7 @@ Description-Content-Type: text/markdown
|
|
|
15
15
|
|
|
16
16
|
# pre-commit-localupdate
|
|
17
17
|
|
|
18
|
-
A CLI tool to automatically update dependencies in `pre-commit-config.yml` files. It specifically targets `additional_dependencies` within local Python, Julia, and Node.js hooks to ensure your tooling stays up-to-date. It also adds version to unversioned packages and pins exact version of loosely defined ones.
|
|
18
|
+
A CLI tool to automatically update dependencies in `pre-commit-config.yml` files. It specifically targets `additional_dependencies` within local Python, Julia, Rust, and Node.js hooks to ensure your tooling stays up-to-date. It also adds version to unversioned packages and pins exact version of loosely defined ones.
|
|
19
19
|
|
|
20
20
|
## Installation
|
|
21
21
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# pre-commit-localupdate
|
|
2
2
|
|
|
3
|
-
A CLI tool to automatically update dependencies in `pre-commit-config.yml` files. It specifically targets `additional_dependencies` within local Python, Julia, and Node.js hooks to ensure your tooling stays up-to-date. It also adds version to unversioned packages and pins exact version of loosely defined ones.
|
|
3
|
+
A CLI tool to automatically update dependencies in `pre-commit-config.yml` files. It specifically targets `additional_dependencies` within local Python, Julia, Rust, and Node.js hooks to ensure your tooling stays up-to-date. It also adds version to unversioned packages and pins exact version of loosely defined ones.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.3.0'
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
# SPDX-License-Identifier: LGPL-3.0-or-later
|
|
3
3
|
|
|
4
4
|
import argparse
|
|
5
|
+
|
|
5
6
|
from pre_commit_localupdate import __version__
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
def parse_args() -> argparse.Namespace:
|
|
9
10
|
"""Parse command line arguments."""
|
|
10
11
|
parser = argparse.ArgumentParser(
|
|
11
|
-
description="Automatically update additional dependencies within local Python and Node.js hooks in pre-commit config files."
|
|
12
|
+
description="Automatically update additional dependencies within local Python, Julia, Rust, and Node.js hooks in pre-commit config files."
|
|
12
13
|
)
|
|
13
14
|
parser.add_argument(
|
|
14
15
|
"--debug",
|
|
@@ -2,17 +2,18 @@
|
|
|
2
2
|
# SPDX-License-Identifier: LGPL-3.0-or-later
|
|
3
3
|
|
|
4
4
|
import logging
|
|
5
|
-
from pathlib import Path
|
|
6
5
|
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
7
|
from typing import List, Tuple
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def load_config_file(file_path: Path) -> Tuple[List[str], str]:
|
|
11
|
+
"""Reads pre-commit configuration file and returns its header lines and content."""
|
|
11
12
|
logging.debug("Reading configuration file: %s", file_path)
|
|
12
13
|
|
|
13
14
|
raw_lines: List[str] = []
|
|
14
15
|
header_lines: List[str] = []
|
|
15
|
-
|
|
16
|
+
content_start_index: int = 0
|
|
16
17
|
|
|
17
18
|
try:
|
|
18
19
|
with file_path.open("r", encoding="utf-8") as f:
|
|
@@ -24,19 +25,19 @@ def load_config_file(file_path: Path) -> Tuple[List[str], str]:
|
|
|
24
25
|
logging.exception("IOError while reading file %s: %s", file_path, exc)
|
|
25
26
|
sys.exit(2)
|
|
26
27
|
|
|
27
|
-
logging.debug("Parsing
|
|
28
|
-
|
|
28
|
+
logging.debug("Parsing file header...")
|
|
29
|
+
content_start_index = 0
|
|
29
30
|
for i, line in enumerate(raw_lines):
|
|
30
31
|
stripped: str = line.strip()
|
|
31
32
|
if stripped == "---":
|
|
32
33
|
logging.debug("YAML marker found.")
|
|
33
|
-
|
|
34
|
+
content_start_index = i + 1
|
|
34
35
|
break
|
|
35
36
|
if stripped and not stripped.startswith("#"):
|
|
36
|
-
|
|
37
|
+
content_start_index = i
|
|
37
38
|
break
|
|
38
39
|
|
|
39
|
-
header_lines = raw_lines[:
|
|
40
|
-
|
|
40
|
+
header_lines = raw_lines[:content_start_index]
|
|
41
|
+
content: str = "".join(raw_lines[content_start_index:])
|
|
41
42
|
|
|
42
|
-
return header_lines,
|
|
43
|
+
return header_lines, content
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 M. Farzalipour Tabriz, Max Planck Institute for Physics
|
|
2
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later
|
|
3
|
+
|
|
4
|
+
from .julia import JuliaPackage
|
|
5
|
+
from .node import NodePackage
|
|
6
|
+
from .package import PackageBase
|
|
7
|
+
from .python import PyPackage
|
|
8
|
+
from .rust import RustPackage
|
|
9
|
+
|
|
10
|
+
SUPPORTED_PACKAGES: dict[str, type[PackageBase]] = {
|
|
11
|
+
"julia": JuliaPackage,
|
|
12
|
+
"node": NodePackage,
|
|
13
|
+
"python": PyPackage,
|
|
14
|
+
"rust": RustPackage,
|
|
15
|
+
}
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
import logging
|
|
5
5
|
import re
|
|
6
6
|
import tomllib
|
|
7
|
+
|
|
7
8
|
import requests
|
|
8
9
|
|
|
9
|
-
from
|
|
10
|
+
from .package import PackageBase
|
|
10
11
|
|
|
11
12
|
# Julia General Registry Source
|
|
12
13
|
JULIA_PKG_API_URL = "https://raw.githubusercontent.com/JuliaRegistries/General/refs/heads/master/{package_name_first_letter}/{package_name}/Versions.toml"
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 M. Farzalipour Tabriz, Max Planck Institute for Physics
|
|
2
|
+
# SPDX-License-Identifier: LGPL-3.0-or-later
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
import re
|
|
6
|
+
from typing import Any, cast
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
|
|
10
|
+
from .package import PackageBase
|
|
11
|
+
|
|
12
|
+
CRATES_API_URL = "https://crates.io/api/v1/crates/{package_name}"
|
|
13
|
+
REQUEST_TIMEOUT = 10
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class RustPackage(PackageBase):
|
|
17
|
+
"""Represents a Rust package.
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
cli: Package is a CLI tool.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
cli: bool = False
|
|
24
|
+
|
|
25
|
+
def __init__(self, **kwargs: Any) -> None:
|
|
26
|
+
self.cli = kwargs.pop("cli", False)
|
|
27
|
+
super().__init__(**kwargs)
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def from_specification(package, spec: str) -> "RustPackage" | None:
|
|
31
|
+
"""Parse a package specification string into a Package object.
|
|
32
|
+
|
|
33
|
+
Handles formats like 'package', 'package:1.2.3', or 'cli:package:1.2.3'.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
spec: The package specification string.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
A Package instance if parsing succeeds, otherwise None.
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
clean_spec = spec.strip().strip('"').strip("'")
|
|
43
|
+
|
|
44
|
+
match = re.match(
|
|
45
|
+
r"^(?:(?P<cli>cli):)?(?P<name>[a-zA-Z0-9_-]+)(?::(?P<version>[^:]+))?$",
|
|
46
|
+
clean_spec,
|
|
47
|
+
)
|
|
48
|
+
if not match:
|
|
49
|
+
logging.warning("Could not parse the package specification: %s", clean_spec)
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
name = match.group("name")
|
|
53
|
+
version = match.group("version")
|
|
54
|
+
cli = True if match.group("cli") else False
|
|
55
|
+
|
|
56
|
+
raw_spec = f":{version}" if version else None
|
|
57
|
+
latest_version = package._get_latest_version(name)
|
|
58
|
+
|
|
59
|
+
return package(
|
|
60
|
+
name=name, raw_spec=raw_spec, latest_version=latest_version, cli=cli
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
@staticmethod
|
|
64
|
+
def _get_latest_version(package_name: str) -> str | None:
|
|
65
|
+
"""Fetch the latest version of a package from crates.io.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
The latest version string if found, otherwise None.
|
|
69
|
+
|
|
70
|
+
"""
|
|
71
|
+
logging.debug("Fetching latest version for %s", package_name)
|
|
72
|
+
try:
|
|
73
|
+
url = CRATES_API_URL.format(package_name=package_name)
|
|
74
|
+
response = requests.get(url, timeout=REQUEST_TIMEOUT)
|
|
75
|
+
response.raise_for_status()
|
|
76
|
+
data = response.json()
|
|
77
|
+
version = data.get("crate", {}).get("max_version")
|
|
78
|
+
if version and isinstance(version, str):
|
|
79
|
+
return cast("str", version)
|
|
80
|
+
|
|
81
|
+
return None
|
|
82
|
+
except requests.exceptions.RequestException as exc:
|
|
83
|
+
logging.warning(
|
|
84
|
+
"Could not fetch latest version for %s: %s", package_name, exc
|
|
85
|
+
)
|
|
86
|
+
return None
|
|
87
|
+
except (KeyError, ValueError) as exc:
|
|
88
|
+
logging.warning("Could not parse response for %s: %s", package_name, exc)
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
def latest_version_specification(package) -> str | None:
|
|
92
|
+
"""Version specification of package pinned to the latest version.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
The latest version specification string if found, otherwise None.
|
|
96
|
+
|
|
97
|
+
"""
|
|
98
|
+
if not package.latest_version:
|
|
99
|
+
return None
|
|
100
|
+
if package.cli:
|
|
101
|
+
return f"cli:{package.name}:{package.latest_version}"
|
|
102
|
+
else:
|
|
103
|
+
return f"{package.name}:{package.latest_version}"
|
|
@@ -3,33 +3,24 @@
|
|
|
3
3
|
|
|
4
4
|
import io
|
|
5
5
|
import logging
|
|
6
|
-
from pathlib import Path
|
|
7
6
|
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
8
|
|
|
9
9
|
import ruamel.yaml
|
|
10
10
|
|
|
11
|
-
from pre_commit_localupdate.node import NodePackage
|
|
12
|
-
from pre_commit_localupdate.package import PackageBase
|
|
13
|
-
from pre_commit_localupdate.python import PyPackage
|
|
14
|
-
from pre_commit_localupdate.julia import JuliaPackage
|
|
15
11
|
from pre_commit_localupdate.io import load_config_file
|
|
16
|
-
|
|
17
|
-
LANGUAGE_PACKAGE_MAP: dict[str, type[PackageBase]] = {
|
|
18
|
-
"python": PyPackage,
|
|
19
|
-
"node": NodePackage,
|
|
20
|
-
"julia": JuliaPackage,
|
|
21
|
-
}
|
|
12
|
+
from pre_commit_localupdate.packages import SUPPORTED_PACKAGES
|
|
22
13
|
|
|
23
14
|
|
|
24
15
|
def update_additional_dependencies(file_path: Path, *, dry_run: bool = False) -> bool:
|
|
25
|
-
"""Reads the configuration file, identifies outdated
|
|
26
|
-
local hooks, and updates them directly in the file.
|
|
16
|
+
"""Reads the pre-commit configuration file, identifies outdated packages
|
|
17
|
+
in local hooks, and updates them directly in the file.
|
|
27
18
|
|
|
28
19
|
Preserves structure, quote styles, the document start marker (---),
|
|
29
20
|
and comments preceding it.
|
|
30
21
|
|
|
31
22
|
Args:
|
|
32
|
-
file_path: Path to the
|
|
23
|
+
file_path: Path to the pre-commit configuration file.
|
|
33
24
|
|
|
34
25
|
Returns:
|
|
35
26
|
True if the file was modified and successfully written, False otherwise.
|
|
@@ -71,23 +62,21 @@ def update_additional_dependencies(file_path: Path, *, dry_run: bool = False) ->
|
|
|
71
62
|
hook_id = hook.get("id")
|
|
72
63
|
logging.debug("Processing hook with ID: %s", hook_id)
|
|
73
64
|
|
|
74
|
-
if hook.get("language") not in
|
|
65
|
+
if hook.get("language") not in SUPPORTED_PACKAGES:
|
|
75
66
|
continue
|
|
76
67
|
|
|
77
|
-
PackageClass = LANGUAGE_PACKAGE_MAP[hook.get("language")]
|
|
78
|
-
|
|
79
68
|
if "additional_dependencies" not in hook:
|
|
80
69
|
continue
|
|
81
70
|
|
|
82
71
|
deps_list = list(hook["additional_dependencies"])
|
|
83
72
|
|
|
73
|
+
Package = SUPPORTED_PACKAGES[hook.get("language")]
|
|
74
|
+
|
|
84
75
|
for i, dep_spec in enumerate(deps_list):
|
|
85
76
|
dep_str = str(dep_spec)
|
|
86
77
|
|
|
87
78
|
logging.debug("Checking dependency: %s", dep_str)
|
|
88
|
-
package
|
|
89
|
-
dep_str
|
|
90
|
-
)
|
|
79
|
+
package = Package.from_specification(dep_str)
|
|
91
80
|
if not package:
|
|
92
81
|
continue
|
|
93
82
|
|
|
@@ -119,7 +108,7 @@ def update_additional_dependencies(file_path: Path, *, dry_run: bool = False) ->
|
|
|
119
108
|
if dry_run:
|
|
120
109
|
sys.exit(1)
|
|
121
110
|
else:
|
|
122
|
-
logging.debug("Writing
|
|
111
|
+
logging.debug("Writing modifications to disk")
|
|
123
112
|
try:
|
|
124
113
|
stream = io.StringIO()
|
|
125
114
|
yaml.dump(config, stream)
|
|
@@ -23,12 +23,12 @@ dependencies = [
|
|
|
23
23
|
"requests>=2.32.5",
|
|
24
24
|
"ruamel-yaml>=0.19.1",
|
|
25
25
|
]
|
|
26
|
-
description = "A CLI tool to automatically update additional dependencies within local Python and Node.js hooks in pre-commit config files."
|
|
26
|
+
description = "A CLI tool to automatically update additional dependencies within local Python, Julia, Rust, and Node.js hooks in pre-commit config files."
|
|
27
27
|
dynamic = []
|
|
28
28
|
name = "pre-commit-localupdate"
|
|
29
29
|
readme = "README.md"
|
|
30
30
|
requires-python = "<3.15,>=3.11"
|
|
31
|
-
version = "0.
|
|
31
|
+
version = "0.3.0"
|
|
32
32
|
|
|
33
33
|
[project.scripts]
|
|
34
34
|
pre-commit-localupdate = "pre_commit_localupdate.__main__:main"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '0.2.0'
|
|
File without changes
|
{pre_commit_localupdate-0.2.0 → pre_commit_localupdate-0.3.0}/pre_commit_localupdate/__main__.py
RENAMED
|
File without changes
|
{pre_commit_localupdate-0.2.0 → pre_commit_localupdate-0.3.0}/pre_commit_localupdate/logs.py
RENAMED
|
File without changes
|