Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions toolgate/scripts/check_file_sizes.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,24 @@
from pathlib import Path

LIMIT = 600
SUFFIXES = (".py", ".ts", ".tsx", ".js", ".mjs")
SUFFIXES = (".py", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".css", ".sh")
ALLOWLIST = Path("file-size-allowlist.txt")


def tracked_sources() -> list[str]:
out = subprocess.run(["git", "ls-files"], capture_output=True, text=True, check=True).stdout
return [path for path in out.splitlines() if path.endswith(SUFFIXES)]
return [path for path in out.splitlines() if path.endswith(SUFFIXES) or _script(path)]


def _script(path: str) -> bool:
"""A file with no suffix that starts with "#!" is a script (toolgate/cli/toolgate)."""
if "." in Path(path).name:
return False
try:
with open(path, "rb") as handle:
return handle.read(2) == b"#!"
except OSError:
return False


def allowlist() -> dict[str, int]:
Expand All @@ -39,7 +50,8 @@ def main() -> int:
allowed = allowlist()
problems = []
for path in tracked_sources():
lines = Path(path).read_text(encoding="utf-8", errors="replace").count("\n")
# splitlines also counts a last line that has no final newline.
lines = len(Path(path).read_text(encoding="utf-8", errors="replace").splitlines())
ceiling = allowed.pop(path, None)
if ceiling is None and lines > LIMIT:
problems.append(f"{path}: {lines} lines, limit {LIMIT}. Split it by concern.")
Expand Down
25 changes: 25 additions & 0 deletions toolgate/tests/test_guard_scripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import sys
from pathlib import Path

import pytest

SCRIPTS = Path(__file__).resolve().parents[1] / "scripts"
FILE_SIZES = SCRIPTS / "check_file_sizes.py"
BOUNDARIES = SCRIPTS / "check_package_boundaries.py"
Expand Down Expand Up @@ -62,6 +64,29 @@ def test_file_one_line_over_the_limit_fails(self, tmp_path):
assert result.returncode == 1
assert "mod.py: 601 lines, limit 600. Split it by concern." in result.stdout

@pytest.mark.parametrize("name", ["App.jsx", "theme.css", "run.sh", "tool.cjs"])
def test_dashboard_styles_and_shell_files_are_measured(self, tmp_path, name):
write(tmp_path, name, source_file(601))
track_everything(tmp_path)
result = run_script(FILE_SIZES, tmp_path)
assert result.returncode == 1
assert f"{name}: 601 lines, limit 600." in result.stdout

def test_a_script_without_a_suffix_is_measured_by_its_shebang(self, tmp_path):
write(tmp_path, "bin/toolgate", "#!/usr/bin/env python3\n" + source_file(600))
write(tmp_path, "LICENSE", source_file(900))
track_everything(tmp_path)
result = run_script(FILE_SIZES, tmp_path)
assert result.returncode == 1
assert result.stdout.strip() == "bin/toolgate: 601 lines, limit 600. Split it by concern."

def test_a_last_line_without_a_newline_counts(self, tmp_path):
write(tmp_path, "mod.py", source_file(600) + "# line 601 without a newline")
track_everything(tmp_path)
result = run_script(FILE_SIZES, tmp_path)
assert result.returncode == 1
assert "mod.py: 601 lines, limit 600." in result.stdout

def test_allowlisted_file_at_its_ceiling_passes(self, tmp_path):
write(tmp_path, "big.py", source_file(700))
write(tmp_path, FILE_SIZE_ALLOWLIST, "# already large when the check arrived\nbig.py 700\n")
Expand Down
Loading