"""Under `pipefail`, an early-exiting `head` must not void the output it kept (#4042). The hooks run with `set -uo pipefail`. `hits=$(git grep -l … | head -4) || hits=""` lost its four hits whenever git grep was still writing when head exited: SIGPIPE, a failed substitution, and the fallback wiped the result. It only showed on big outputs, so the by-name duplicate arm went silent for exactly the most-duplicated names. Two halves: the real function on a repo big enough to overflow the pipe, and the check_plugin pattern that keeps the shape out of every hook (shown able to fail, rule #167). """ from __future__ import annotations import os import shutil import subprocess from pathlib import Path import pytest ROOT = Path(__file__).resolve().parents[1] DEFS = ROOT / "plugin" / "hooks" / "scribe_defs.sh" def test_the_duplicate_arm_still_names_a_name_defined_in_thousands_of_files(tmp_path): for tool in ("git", "bash"): if shutil.which(tool) is None: pytest.skip(f"{tool!r} not installed") env = {"PATH": os.environ["PATH"], "HOME": str(tmp_path), "GIT_AUTHOR_NAME": "t", "GIT_AUTHOR_EMAIL": "t@x", "GIT_COMMITTER_NAME": "t", "GIT_COMMITTER_EMAIL": "t@x"} repo = tmp_path / "repo" repo.mkdir() subprocess.run(["git", "init", "-q"], cwd=repo, check=True, env=env) # ~3000 × ~60 bytes of `git grep -l` output: well past a 64 KiB pipe buffer, # so git grep is still writing when head has its four lines. for i in range(3000): (repo / f"module_with_a_fairly_long_descriptive_name_{i}.py").write_text("def slug(t):\n return t\n") subprocess.run(["git", "add", "."], cwd=repo, check=True, env=env) subprocess.run(["git", "commit", "-q", "-m", "base"], cwd=repo, check=True, env=env) script = f'set -uo pipefail\n. "{DEFS}"\nprintf "sym\\tslug\\n" | scribe_local_dups "{repo}" new.py\n' out = subprocess.run(["bash", "-c", script], capture_output=True, text=True, env=env, timeout=60) assert out.returncode == 0, out.stderr assert "`slug` is already defined in 4 other file(s)" in out.stdout def test_the_lint_pattern_catches_the_shape_and_passes_the_fix(): from scripts.check_plugin import PATTERNS pattern = next(p for p, label, _why in PATTERNS if label.startswith("early-exit consumer")) for bad in ('hits=$(git grep -l x | head -4) || hits=""', " | grep -m1 -v '^[[:space:]]*$') || old_first=\"\"", 'x=$(a | grep -q b) || x=""'): assert pattern.search(bad), bad for good in ('hits=$(git grep -l x | head -4 || true)', 'ln=$(grep -nF -m1 -- "$a" "$f" | cut -d: -f1) || ln=""'): assert not pattern.search(good), good