mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-10 20:27:15 +01:00
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file length / file-length (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
palette drift / palette (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
shared gate tests / gate unit tests (push) Waiting to run
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
120 lines
3.3 KiB
Python
120 lines
3.3 KiB
Python
"""Stack detection and a bounded "does this repo have tests" probe."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from pathlib import Path
|
|
|
|
PRUNE = frozenset(
|
|
{
|
|
".git",
|
|
".utils",
|
|
"node_modules",
|
|
"build",
|
|
".dart_tool",
|
|
".venv",
|
|
"venv",
|
|
"target",
|
|
"__pycache__",
|
|
".gradle",
|
|
".idea",
|
|
"dist",
|
|
".mypy_cache",
|
|
".ruff_cache",
|
|
".pytest_cache",
|
|
"Pods",
|
|
".godot",
|
|
"vendor",
|
|
"third_party",
|
|
}
|
|
)
|
|
MAX_DEPTH = 4
|
|
MAX_FILES = 5000
|
|
|
|
# stack -> marker files at the repo root (checked in order of this table).
|
|
MARKERS: tuple[tuple[str, tuple[str, ...]], ...] = (
|
|
("flutter", ("pubspec.yaml",)),
|
|
("godot", ("project.godot",)),
|
|
(
|
|
"gradle",
|
|
("build.gradle", "build.gradle.kts", "settings.gradle", "settings.gradle.kts"),
|
|
),
|
|
("python", ("pyproject.toml", "setup.py", "setup.cfg", "requirements.txt")),
|
|
("node", ("package.json",)),
|
|
("go", ("go.mod",)),
|
|
("rust", ("Cargo.toml",)),
|
|
)
|
|
|
|
|
|
def _walk(repo: Path) -> list[Path]:
|
|
"""Files under `repo` (pruned, depth- and count-bounded; never follows links)."""
|
|
found: list[Path] = []
|
|
base = len(repo.parts)
|
|
for root, dirs, files in os.walk(repo, followlinks=False):
|
|
dirs[:] = [d for d in dirs if d not in PRUNE]
|
|
if len(Path(root).parts) - base >= MAX_DEPTH:
|
|
dirs[:] = []
|
|
found.extend(Path(root, f) for f in files)
|
|
if len(found) >= MAX_FILES:
|
|
break
|
|
return found
|
|
|
|
|
|
def detect_stacks(repo: Path) -> list[str]:
|
|
"""Stacks present in `repo`, most specific first; `shell` if only scripts."""
|
|
stacks = [s for s, names in MARKERS if any((repo / n).is_file() for n in names)]
|
|
if not stacks and any(p.suffix == ".sh" for p in _walk(repo)):
|
|
stacks.append("shell")
|
|
return stacks
|
|
|
|
|
|
def _has_pattern(
|
|
files: list[Path], suffixes: tuple[str, ...], names: tuple[str, ...]
|
|
) -> bool:
|
|
"""True when some file ends with a test suffix or sits under a test dir."""
|
|
for path in files:
|
|
if path.name.endswith(suffixes) or path.name.startswith(names):
|
|
return True
|
|
return False
|
|
|
|
|
|
def has_tests(repo: Path, stacks: list[str]) -> bool:
|
|
"""Whether the repo ships any automated tests (best-effort, bounded)."""
|
|
files = _walk(repo)
|
|
rels = [p.relative_to(repo) for p in files]
|
|
in_test_dir = any(
|
|
part in {"tests", "test", "spec", "__tests__"}
|
|
for r in rels
|
|
for part in r.parts[:-1]
|
|
)
|
|
named = _has_pattern(
|
|
files,
|
|
(
|
|
"_test.py",
|
|
"_test.dart",
|
|
".test.ts",
|
|
".test.js",
|
|
".test.tsx",
|
|
".spec.ts",
|
|
".spec.js",
|
|
"_test.go",
|
|
"Test.kt",
|
|
"Test.java",
|
|
".bats",
|
|
"_test.gd",
|
|
),
|
|
("test_",),
|
|
)
|
|
return in_test_dir or named or _node_test_script(repo, stacks)
|
|
|
|
|
|
def _node_test_script(repo: Path, stacks: list[str]) -> bool:
|
|
"""A real `scripts.test` in package.json (not npm's placeholder)."""
|
|
if "node" not in stacks:
|
|
return False
|
|
try:
|
|
text = (repo / "package.json").read_text(encoding="utf-8")
|
|
except (OSError, UnicodeDecodeError):
|
|
return False
|
|
return '"test"' in text and "no test specified" not in text
|