Files
MeshTerm/tests/test_spdx.py
T
Jean-Pierre MartineauandClaude Fable 5.1 e7fb14bed0 The SPDX gate fails on a file it cannot read instead of skipping it, and the changelog says licence
A silent skip would let an unmarked file through, which is the one thing the test exists to
catch. The two new changelog lines now read in the file's own voice, and a docstring in the
logging tests is wrapped under the line limit.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-13 13:15:06 -04:00

47 lines
2.0 KiB
Python

# SPDX-License-Identifier: Apache-2.0
"""Enforce SPDX license identifiers on all Python files.
Every Python source file in the MeshTerm project must declare its license with
a SPDX-License-Identifier comment on line 1 (or line 2 if it starts with a
shebang). This test walks the entire codebase and asserts the identifier is
present. A file lifted out of the repository carries no license marking
otherwise, so this gate ensures no file lands unmarked.
"""
from pathlib import Path
def test_spdx_headers():
"""Every Python file must have an SPDX-License-Identifier header."""
repo_root = Path(__file__).parent.parent
python_files = []
# Collect all Python files from the required directories
for directory in ["meshterm", "tests", "packaging", "scripts/xiao-radio"]:
dir_path = repo_root / directory
if dir_path.exists():
if directory == "packaging":
# For packaging, include both .py files and .spec files
python_files.extend(dir_path.glob("*.py"))
python_files.extend(dir_path.glob("*.spec"))
else:
python_files.extend(dir_path.rglob("*.py"))
# Also check the extensionless scripts/meshterm-spi-bridge script
spi_bridge = repo_root / "scripts" / "meshterm-spi-bridge"
if spi_bridge.exists():
python_files.append(spi_bridge)
missing_spdx = []
for filepath in sorted(python_files):
# A file that cannot be read as UTF-8 is a finding, not something to skip: every
# source file here is UTF-8, and a silent skip would let an unmarked file through.
head = filepath.read_text(encoding="utf-8").split("\n", 2)[:2]
if not any("SPDX-License-Identifier:" in line for line in head):
missing_spdx.append(str(filepath.relative_to(repo_root)))
assert not missing_spdx, (
f"Found {len(missing_spdx)} Python file(s) without SPDX-License-Identifier:\n"
+ "\n".join(f" {f}" for f in missing_spdx)
)