lange-python 0.3.13__tar.gz → 0.3.15__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.
- {lange_python-0.3.13 → lange_python-0.3.15}/PKG-INFO +1 -1
- {lange_python-0.3.13 → lange_python-0.3.15}/lange/cli/code/__init__.py +4 -1
- lange_python-0.3.15/lange/cli/code/audit/__init__.py +5 -0
- lange_python-0.3.15/lange/cli/code/audit/_command.py +64 -0
- lange_python-0.3.15/lange/cli/code/audit/_discovery.py +70 -0
- lange_python-0.3.15/lange/cli/code/audit/_runner.py +31 -0
- lange_python-0.3.15/lange/cli/code/audit/_types.py +10 -0
- {lange_python-0.3.13 → lange_python-0.3.15}/pyproject.toml +1 -1
- {lange_python-0.3.13 → lange_python-0.3.15}/README.md +0 -0
- {lange_python-0.3.13 → lange_python-0.3.15}/lange/__init__.py +0 -0
- {lange_python-0.3.13 → lange_python-0.3.15}/lange/__main__.py +0 -0
- {lange_python-0.3.13 → lange_python-0.3.15}/lange/_util/__init__.py +0 -0
- {lange_python-0.3.13 → lange_python-0.3.15}/lange/_util/_base_client.py +0 -0
- {lange_python-0.3.13 → lange_python-0.3.15}/lange/_util/_key_handling.py +0 -0
- {lange_python-0.3.13 → lange_python-0.3.15}/lange/cli/__init__.py +0 -0
- {lange_python-0.3.13 → lange_python-0.3.15}/lange/cli/build/__init__.py +0 -0
- {lange_python-0.3.13 → lange_python-0.3.15}/lange/cli/build/_command.py +0 -0
- {lange_python-0.3.13 → lange_python-0.3.15}/lange/cli/build/_discovery.py +0 -0
- {lange_python-0.3.13 → lange_python-0.3.15}/lange/cli/build/_docker.py +0 -0
- {lange_python-0.3.13 → lange_python-0.3.15}/lange/cli/build/_poetry.py +0 -0
- {lange_python-0.3.13 → lange_python-0.3.15}/lange/cli/build/_types.py +0 -0
- {lange_python-0.3.13 → lange_python-0.3.15}/lange/cli/code/_stats.py +0 -0
- {lange_python-0.3.13 → lange_python-0.3.15}/lange/cli/distribution/__init__.py +0 -0
- {lange_python-0.3.13 → lange_python-0.3.15}/lange/cli/distribution/_command.py +0 -0
- {lange_python-0.3.13 → lange_python-0.3.15}/lange/distribution/__init__.py +0 -0
- {lange_python-0.3.13 → lange_python-0.3.15}/lange/distribution/_client.py +0 -0
- {lange_python-0.3.13 → lange_python-0.3.15}/lange/distribution/_update_macos.py +0 -0
- {lange_python-0.3.13 → lange_python-0.3.15}/lange/distribution/_util.py +0 -0
- {lange_python-0.3.13 → lange_python-0.3.15}/lange/tunnel/__init__.py +0 -0
- {lange_python-0.3.13 → lange_python-0.3.15}/lange/tunnel/_client.py +0 -0
- {lange_python-0.3.13 → lange_python-0.3.15}/lange/tunnel/_util.py +0 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import click
|
|
2
2
|
|
|
3
|
+
from .audit import code_audit
|
|
3
4
|
from ._stats import code_stats
|
|
4
5
|
|
|
6
|
+
|
|
5
7
|
@click.group()
|
|
6
8
|
def code_group() -> None:
|
|
7
9
|
"""
|
|
@@ -10,4 +12,5 @@ def code_group() -> None:
|
|
|
10
12
|
:returns: ``None``.
|
|
11
13
|
"""
|
|
12
14
|
|
|
13
|
-
code_group.add_command(code_stats,"stats")
|
|
15
|
+
code_group.add_command(code_stats, "stats")
|
|
16
|
+
code_group.add_command(code_audit, "audit")
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""CLI command orchestration for ``lange code audit``."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import subprocess
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
|
|
10
|
+
from ._discovery import (
|
|
11
|
+
detect_available_audit_tools,
|
|
12
|
+
list_auditable_folders,
|
|
13
|
+
resolve_audit_folder,
|
|
14
|
+
)
|
|
15
|
+
from ._runner import run_audit_command
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@click.command("audit")
|
|
19
|
+
@click.argument("folder_name", required=False)
|
|
20
|
+
def code_audit(folder_name: str | None) -> None:
|
|
21
|
+
"""
|
|
22
|
+
Run dependency audits for one folder or all auditable top-level folders.
|
|
23
|
+
|
|
24
|
+
:param folder_name: Optional folder name to audit.
|
|
25
|
+
:returns: ``None``.
|
|
26
|
+
"""
|
|
27
|
+
if folder_name:
|
|
28
|
+
target_folder = resolve_audit_folder(folder_name=folder_name, root=Path.cwd())
|
|
29
|
+
run_audits_for_folder(folder=target_folder)
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
target_folders = list_auditable_folders(Path.cwd())
|
|
33
|
+
if not target_folders:
|
|
34
|
+
raise click.ClickException(
|
|
35
|
+
"No auditable services were found in the current directory."
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
for target_folder in target_folders:
|
|
39
|
+
run_audits_for_folder(folder=target_folder)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def run_audits_for_folder(folder: Path) -> None:
|
|
43
|
+
"""
|
|
44
|
+
Detect and execute all supported audit commands for one folder.
|
|
45
|
+
|
|
46
|
+
:param folder: Folder that should be audited.
|
|
47
|
+
:returns: ``None``.
|
|
48
|
+
"""
|
|
49
|
+
audit_tools = detect_available_audit_tools(folder)
|
|
50
|
+
if not audit_tools:
|
|
51
|
+
raise click.ClickException(
|
|
52
|
+
"Could not detect a supported audit tool. "
|
|
53
|
+
"Expected pnpm-lock, pnpm-lock.yaml and/or uv.lock."
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
for audit_tool in audit_tools:
|
|
58
|
+
run_audit_command(folder=folder, audit_tool=audit_tool)
|
|
59
|
+
except subprocess.CalledProcessError as error:
|
|
60
|
+
raise click.ClickException(
|
|
61
|
+
f"Audit command failed with exit code {error.returncode}."
|
|
62
|
+
) from error
|
|
63
|
+
except OSError as error:
|
|
64
|
+
raise click.ClickException(str(error)) from error
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Folder and audit-tool discovery helpers for ``lange code audit``."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
from ._types import AuditTool, PNPM_AUDIT_TOOL, UV_AUDIT_TOOL
|
|
10
|
+
|
|
11
|
+
PNPM_LOCK_FILES: tuple[str, ...] = ("pnpm-lock", "pnpm-lock.yaml")
|
|
12
|
+
UV_LOCK_FILE = "uv.lock"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def list_candidate_folders(root: Path) -> list[Path]:
|
|
16
|
+
"""
|
|
17
|
+
List top-level non-hidden directories.
|
|
18
|
+
|
|
19
|
+
:param root: Directory that should be scanned.
|
|
20
|
+
:returns: Sorted non-hidden child directories.
|
|
21
|
+
"""
|
|
22
|
+
candidates = [
|
|
23
|
+
path
|
|
24
|
+
for path in root.iterdir()
|
|
25
|
+
if path.is_dir() and not path.name.startswith(".")
|
|
26
|
+
]
|
|
27
|
+
return sorted(candidates, key=lambda item: item.name.lower())
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def resolve_audit_folder(folder_name: str, root: Path) -> Path:
|
|
31
|
+
"""
|
|
32
|
+
Resolve an explicitly requested audit folder.
|
|
33
|
+
|
|
34
|
+
:param folder_name: Folder argument passed via CLI.
|
|
35
|
+
:param root: Current working directory.
|
|
36
|
+
:returns: Resolved target folder path.
|
|
37
|
+
"""
|
|
38
|
+
folder = (root / folder_name).resolve()
|
|
39
|
+
if not folder.exists() or not folder.is_dir():
|
|
40
|
+
raise click.ClickException(
|
|
41
|
+
f"Folder '{folder_name}' was not found or is not a directory."
|
|
42
|
+
)
|
|
43
|
+
return folder
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def list_auditable_folders(root: Path) -> list[Path]:
|
|
47
|
+
"""
|
|
48
|
+
List top-level folders that contain at least one supported audit lock file.
|
|
49
|
+
|
|
50
|
+
:param root: Directory that should be scanned.
|
|
51
|
+
:returns: Sorted list of auditable folders.
|
|
52
|
+
"""
|
|
53
|
+
return [
|
|
54
|
+
folder for folder in list_candidate_folders(root) if detect_available_audit_tools(folder)
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def detect_available_audit_tools(folder: Path) -> list[AuditTool]:
|
|
59
|
+
"""
|
|
60
|
+
Detect supported audit tools available in the given folder.
|
|
61
|
+
|
|
62
|
+
:param folder: Folder that should be inspected.
|
|
63
|
+
:returns: Ordered list of detected audit tools.
|
|
64
|
+
"""
|
|
65
|
+
available: list[AuditTool] = []
|
|
66
|
+
if any((folder / lock_file).is_file() for lock_file in PNPM_LOCK_FILES):
|
|
67
|
+
available.append(PNPM_AUDIT_TOOL)
|
|
68
|
+
if (folder / UV_LOCK_FILE).is_file():
|
|
69
|
+
available.append(UV_AUDIT_TOOL)
|
|
70
|
+
return available
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Command execution helpers for ``lange code audit``."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import subprocess
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from ._types import AuditTool, PNPM_AUDIT_TOOL, UV_AUDIT_TOOL
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def build_audit_command(audit_tool: AuditTool) -> list[str]:
|
|
12
|
+
"""
|
|
13
|
+
Build the subprocess command for one supported audit tool.
|
|
14
|
+
|
|
15
|
+
:param audit_tool: Tool that should be executed.
|
|
16
|
+
:returns: Command arguments for ``subprocess.run``.
|
|
17
|
+
"""
|
|
18
|
+
if audit_tool == PNPM_AUDIT_TOOL:
|
|
19
|
+
return ["pnpm", "audit"]
|
|
20
|
+
return ["uv", "audit"]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def run_audit_command(folder: Path, audit_tool: AuditTool) -> None:
|
|
24
|
+
"""
|
|
25
|
+
Execute one audit command in the given folder.
|
|
26
|
+
|
|
27
|
+
:param folder: Working directory for the audit command.
|
|
28
|
+
:param audit_tool: Tool that should be executed.
|
|
29
|
+
:returns: ``None``.
|
|
30
|
+
"""
|
|
31
|
+
subprocess.run(build_audit_command(audit_tool), check=True, cwd=folder)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|