ci: gate repository root against unbounded file growth

Co-Authored-By: Ishaan Jaffer <155045088+ishaan-berri@users.noreply.github.com>
This commit is contained in:
Devin AI 2026-07-08 18:53:50 +00:00
parent 86a9871ae9
commit e1609d43da
3 changed files with 136 additions and 0 deletions

View file

@ -0,0 +1,36 @@
name: Check Root File Count
on:
pull_request:
branches:
- main
- litellm_internal_staging
- litellm_oss_staging
- "litellm_**"
merge_group:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
check-root-file-count:
runs-on: ubuntu-latest
timeout-minutes: 5
env:
MAX_ROOT_FILES: "44"
steps:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
with:
python-version: "3.12"
- name: Check root file count
run: python scripts/check_root_file_count.py "$MAX_ROOT_FILES"

View file

@ -0,0 +1,60 @@
import subprocess
import sys
from dataclasses import dataclass
@dataclass(frozen=True, slots=True)
class RootFileCheck:
root_files: tuple[str, ...]
max_root_files: int
@property
def count(self) -> int:
return len(self.root_files)
@property
def within_limit(self) -> bool:
return self.count <= self.max_root_files
def root_files_of(tracked_files: tuple[str, ...]) -> tuple[str, ...]:
return tuple(path for path in tracked_files if "/" not in path)
def list_tracked_files() -> tuple[str, ...]:
completed = subprocess.run(
["git", "ls-files", "-z"],
capture_output=True,
check=True,
text=True,
)
return tuple(path for path in completed.stdout.split("\0") if path)
def report(check: RootFileCheck) -> str:
if check.within_limit:
return f"Root file count OK: {check.count} tracked file(s) <= limit {check.max_root_files}"
listing = "\n".join(f" {path}" for path in sorted(check.root_files))
return (
f"::error::Too many files in the repository root: {check.count} tracked "
f"file(s), limit is {check.max_root_files}\n"
f"{listing}\n"
"Move new files into an appropriate subdirectory instead of the repo root. "
"If a new root file is genuinely required, raise MAX_ROOT_FILES in "
".github/workflows/check-root-file-count.yml in the same PR so the bump is "
"reviewed"
)
def main(argv: tuple[str, ...]) -> int:
max_root_files = int(argv[1])
check = RootFileCheck(
root_files=root_files_of(list_tracked_files()),
max_root_files=max_root_files,
)
print(report(check))
return 0 if check.within_limit else 1
if __name__ == "__main__":
sys.exit(main(tuple(sys.argv)))

View file

@ -0,0 +1,40 @@
import importlib.util
from pathlib import Path
_MODULE_PATH = Path(__file__).resolve().parents[2] / "scripts" / "check_root_file_count.py"
_spec = importlib.util.spec_from_file_location("check_root_file_count", _MODULE_PATH)
checker = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(checker)
def test_root_files_of_keeps_only_top_level_paths():
tracked = (
"README.md",
".gitignore",
"litellm/main.py",
"scripts/check_root_file_count.py",
"docs/my-website/sidebars.js",
)
assert checker.root_files_of(tracked) == ("README.md", ".gitignore")
def test_count_equal_to_limit_is_within_limit():
check = checker.RootFileCheck(root_files=("a", "b", "c"), max_root_files=3)
assert check.count == 3
assert check.within_limit is True
def test_count_over_limit_fails_and_lists_offenders():
check = checker.RootFileCheck(root_files=("zzz.sh", "aaa.py", "bbb.json"), max_root_files=2)
assert check.within_limit is False
message = checker.report(check)
assert "::error::" in message
assert message.index("aaa.py") < message.index("bbb.json") < message.index("zzz.sh")
assert "MAX_ROOT_FILES" in message
def test_report_ok_message_when_within_limit():
check = checker.RootFileCheck(root_files=("a", "b"), max_root_files=44)
message = checker.report(check)
assert "OK" in message
assert "::error::" not in message