mirror of
https://github.com/bmad-code-org/BMAD-METHOD.git
synced 2026-09-19 08:11:52 +08:00
40c4572a31
* build: declare the Python toolchain in pyproject.toml Pin the dev environment (pytest, pytest-xdist, pyyaml, ruamel.yaml, ruff) with uv instead of a one-line --with list in package.json. The project is marked non-packaged: the tree itself is what gets installed, and shipped scripts keep their inline metadata. The validate CI job now runs on uv alone, without Node, and gains a ruff check. * style: apply ruff to tools and skill scripts Mechanical output of ruff check --fix and ruff format under the new config, plus four hand edits: explicit zip strictness in setup.py and test_sprint_status.py, an f-string for the hex colour in brain.py, and a noqa on the sys.path-dependent import in render_skill.py. No behaviour change; 500 tests pass. * build: declare Python 3.11 as the floor of every shipped script Eighteen scripts declared 3.8 or 3.10 inline while ruff targeted 3.11, so an autofix could introduce syntax those scripts did not advertise. The lower floors were never reachable: every skill that carried one also runs a 3.11 hub script, and uv fetches whatever interpreter a script asks for. All inline metadata now says 3.11, and a test keeps the inline floors equal to requires-python and the ruff target.
49 lines
1.8 KiB
Python
49 lines
1.8 KiB
Python
"""Every shipped script declares the same Python floor as pyproject.toml.
|
|
|
|
Scripts run through `uv run`, which reads their inline (PEP 723) metadata,
|
|
while ruff reads only pyproject.toml. Keeping the two equal means lint
|
|
targets exactly the runtime the scripts advertise.
|
|
"""
|
|
|
|
import re
|
|
import tomllib
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[2]
|
|
SCAN_DIRS = ("skills", "tools")
|
|
INLINE_BLOCK = re.compile(r"^# /// script\n(?P<body>(?:^#(?: .*)?\n)+?)^# ///$", re.MULTILINE)
|
|
REQUIRES = re.compile(r'^#\s*requires-python\s*=\s*"([^"]*)"', re.MULTILINE)
|
|
|
|
|
|
def declared_floor(path: Path) -> str | None:
|
|
match = INLINE_BLOCK.search(path.read_text(encoding="utf-8"))
|
|
if not match:
|
|
return None
|
|
found = REQUIRES.search(match.group("body"))
|
|
return found.group(1) if found else None
|
|
|
|
|
|
class PythonFloorTest(unittest.TestCase):
|
|
def test_inline_metadata_matches_project(self):
|
|
config = tomllib.loads((REPO_ROOT / "pyproject.toml").read_text(encoding="utf-8"))
|
|
project_floor = config["project"]["requires-python"]
|
|
target = config["tool"]["ruff"]["target-version"]
|
|
self.assertEqual(target, "py" + project_floor.removeprefix(">=").replace(".", ""))
|
|
mismatches = []
|
|
seen = 0
|
|
for scan in SCAN_DIRS:
|
|
for path in sorted((REPO_ROOT / scan).rglob("*.py")):
|
|
floor = declared_floor(path)
|
|
if floor is None:
|
|
continue
|
|
seen += 1
|
|
if floor != project_floor:
|
|
mismatches.append(f"{path.relative_to(REPO_ROOT).as_posix()}: {floor}")
|
|
self.assertGreater(seen, 0, "no scripts with inline metadata found")
|
|
self.assertEqual(mismatches, [], "\n".join(mismatches))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|