mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-13 23:11:40 +00:00
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:
parent
86a9871ae9
commit
e1609d43da
3 changed files with 136 additions and 0 deletions
36
.github/workflows/check-root-file-count.yml
vendored
Normal file
36
.github/workflows/check-root-file-count.yml
vendored
Normal 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"
|
||||
60
scripts/check_root_file_count.py
Normal file
60
scripts/check_root_file_count.py
Normal 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)))
|
||||
40
tests/test_litellm/test_check_root_file_count.py
Normal file
40
tests/test_litellm/test_check_root_file_count.py
Normal 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
|
||||
Loading…
Add table
Reference in a new issue