4"""Runtime fixtures for immutable hook ownership and candidate dispatch."""
6from __future__
import annotations
15from collections.abc
import Callable
16from contextlib
import suppress
17from dataclasses
import dataclass
18from pathlib
import Path
20sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
22from scripts.dev.git_environment
import sanitized_git_environment, trusted_git_executable
24REPO_ROOT = Path(__file__).resolve().parents[2]
25INSTALLER = REPO_ROOT /
"scripts/git/install-hooks.sh"
26LAUNCHER = REPO_ROOT /
"scripts/git/hook-launcher"
27PRE_COMMIT = REPO_ROOT /
"scripts/git/pre-commit"
28HOOKS_JUST = REPO_ROOT /
"just/hooks.just"
29CI_SCRIPT = REPO_ROOT /
"scripts/ci.sh"
30PROOF_WRITER = REPO_ROOT /
"scripts/git/write-proof.py"
64class RuntimeSelftestError(RuntimeError):
65 """One runtime hook invariant failed."""
68def _fail(message: str) ->
None:
69 raise RuntimeSelftestError(message)
72def default_signal_test_command(*command: str) -> tuple[str, ...]:
73 """Wrap one test child so an asynchronous parent cannot mask its signals."""
75 _fail(
"default-signal fixture command is empty")
80for item in (signal.SIGHUP, signal.SIGINT, signal.SIGQUIT, signal.SIGTERM):
81 signal.signal(item, signal.SIG_DFL)
82os.execv(sys.argv[1], sys.argv[1:])
84 return (sys.executable,
"-I",
"-c", program, *command)
87@dataclass(frozen=True)
89 """One fully specified invocation of a private selftest script."""
91 command: tuple[str, ...]
93 environment: dict[str, str]
94 pass_fds: tuple[int, ...] = ()
96 preexec_fn: Callable[[],
None] |
None =
None
99def _run_private(spec: PrivateRun) -> subprocess.CompletedProcess[str]:
100 """Run one repository-owned or generated private fixture."""
101 return subprocess.run(
104 env=spec.environment,
105 pass_fds=spec.pass_fds,
107 preexec_fn=spec.preexec_fn,
116 root: Path, *args: str, input_data: bytes |
None =
None
117) -> subprocess.CompletedProcess[bytes]:
118 """Run trusted Git in one private fixture without assuming its status."""
119 return subprocess.run(
120 [trusted_git_executable(),
"-C", str(root), *args],
121 env=sanitized_git_environment(),
128def _git(root: Path, *args: str, input_data: bytes |
None =
None) -> bytes:
129 proc = _git_result(root, *args, input_data=input_data)
131 _fail(proc.stderr.decode(errors=
"replace").strip())
135def _write(path: Path, text: str, *, executable: bool =
False) ->
None:
136 path.parent.mkdir(parents=
True, exist_ok=
True)
137 path.write_text(text, encoding=
"utf-8")
142def _init_repo(root: Path) ->
None:
143 _git(root,
"init",
"--quiet")
144 _git(root,
"config",
"user.email",
"selftest@invalid")
145 _git(root,
"config",
"user.name",
"selftest")
148def _owner_text(hook: str, label: str =
"") -> str:
149 return f
"#!/usr/bin/env bash\nset -euo pipefail\nprintf '%s\\0' '{label}{hook}' \"$@\"\n"
152def _seed_launcher_repo(root: Path, *, include_launcher: bool =
True) ->
None:
155 (root /
"scripts/git").mkdir(parents=
True, exist_ok=
True)
156 shutil.copy2(LAUNCHER, root /
"scripts/git/hook-launcher")
157 for hook
in HOOK_NAMES:
158 _write(root / f
"scripts/git/{hook}", _owner_text(hook), executable=
True)
159 _git(root,
"add",
".")
160 _git(root,
"commit",
"--quiet",
"-m",
"fixture")
165 environment: dict[str, str] |
None =
None,
166 installer: Path = INSTALLER,
167) -> subprocess.CompletedProcess[str]:
170 default_signal_test_command(
"/bin/bash",
"-p", str(installer)),
172 sanitized_git_environment()
if environment
is None else environment,
177def _managed_dir(root: Path) -> Path:
178 common = os.fsdecode(_git(root,
"rev-parse",
"--path-format=absolute",
"--git-common-dir"))
179 return Path(common.strip()) /
"ra8-hooks"
183 root: Path, hook: str, args: tuple[str, ...], environment: dict[str, str] |
None =
None
185 proc = subprocess.run(
186 [str(_managed_dir(root) / hook), *args],
188 env=sanitized_git_environment()
if environment
is None else environment,
194 _fail(f
"{hook} launcher returned {proc.returncode}: {proc.stderr!r}")
198def _assert_argv_forwarding(root: Path) ->
None:
199 args = (
"plain",
"path with spaces",
"line one\nline two")
200 for hook
in HOOK_NAMES:
201 expected = b
"\0".join(item.encode()
for item
in (hook, *args)) + b
"\0"
202 actual = _run_launcher(root, hook, args)
203 if actual != expected:
204 _fail(f
"{hook} launcher corrupted argv: {actual!r}")
207def _launcher_path_case(root: Path) ->
None:
208 fake_bin = root /
".venv/bin"
209 git_marker = root /
".git/fake-git"
210 core_markers = tuple(
211 root / f
".git/fake-{name}"
212 for name
in (
"chmod",
"env",
"ln",
"mkdir",
"mktemp",
"readlink",
"rm")
216 f
"#!/bin/bash\nprintf 'ran\\n' >{git_marker!s}\nexit 99\n",
219 for marker
in core_markers:
220 name = marker.name.removeprefix(
"fake-")
223 f
"#!/bin/bash -p\nprintf 'ran\\n' >{marker!s}\nexit 99\n",
226 environment = sanitized_git_environment()
227 environment[
"PATH"] = f
"{fake_bin}:{environment.get('PATH', os.defpath)}"
228 actual = _run_launcher(root,
"pre-commit", (), environment)
229 if actual != b
"pre-commit\0":
230 _fail(
"launcher PATH hardening changed hook output")
231 if git_marker.exists()
or any(marker.exists()
for marker
in core_markers):
232 _fail(
"launcher executed a source-tree Git or core-utility shim")
235def _installer_path_case(base: Path) ->
None:
236 """Prove installation never resolves mutable core tools through PATH."""
237 root, fake_bin = base /
"installer-path", base /
"installer-path-bin"
238 marker_dir = base /
"installer-path-markers"
242 _seed_launcher_repo(root)
243 for name
in (
"chmod",
"cp",
"grep",
"mkdir",
"mktemp",
"mv",
"rm",
"rmdir"):
246 f
"#!/bin/bash -p\nprintf 'ran\\n' >{marker_dir!s}/${{0##*/}}\nexit 99\n",
249 environment = sanitized_git_environment()
250 environment[
"PATH"] = f
"{fake_bin}:{environment.get('PATH', os.defpath)}"
251 result = _run_installer(root, environment)
252 if result.returncode:
253 _fail(f
"installer PATH isolation failed: {result.stderr}")
254 if tuple(marker_dir.iterdir()):
255 _fail(
"installer executed an arbitrary-PATH core utility")
258def _launcher_immutability_case(base: Path) ->
None:
259 root = base /
"launcher"
260 linked = base /
"linked worktree"
262 _seed_launcher_repo(root)
263 _git(root,
"branch",
"linked-branch")
264 _git(root,
"worktree",
"add",
"--quiet", str(linked),
"linked-branch")
265 _write(linked /
"scripts/git/pre-commit", _owner_text(
"pre-commit",
"linked:"), executable=
True)
266 _git(linked,
"add",
"scripts/git/pre-commit")
267 _git(linked,
"commit",
"--quiet",
"-m",
"linked owner")
268 result = _run_installer(root)
269 if result.returncode:
270 _fail(f
"launcher install failed: {result.stderr}")
271 managed = _managed_dir(root)
272 configured = os.fsdecode(_git(root,
"config",
"--local",
"--get",
"core.hooksPath")).strip()
273 if configured != str(managed)
or _managed_dir(linked) != managed:
274 _fail(
"linked worktrees did not share the managed common-dir hook path")
275 _assert_argv_forwarding(root)
276 if not _run_launcher(linked,
"pre-commit", ()).startswith(b
"linked:pre-commit\0"):
277 _fail(
"shared launcher did not select the linked worktree HEAD")
278 _write(root /
"scripts/git/pre-commit", _owner_text(
"pre-commit",
"mutable:"), executable=
True)
279 _launcher_path_case(root)
280 _git(root,
"add",
"scripts/git/pre-commit")
281 if _run_launcher(root,
"pre-commit", ()).startswith(b
"mutable:"):
282 _fail(
"staged worktree hook ran instead of immutable HEAD")
283 _write(root /
"scripts/git/pre-commit", _owner_text(
"pre-commit"), executable=
True)
284 _git(root,
"add",
"scripts/git/pre-commit")
285 _unmanaged_install_cases(root)
288def _unmanaged_install_cases(root: Path) ->
None:
289 managed = _managed_dir(root)
290 unmanaged = root /
"unmanaged-hooks"
291 _git(root,
"config",
"--local",
"core.hooksPath", str(unmanaged))
292 result = _run_installer(root)
293 if result.returncode == 0:
294 _fail(
"installer replaced an unmanaged core.hooksPath")
295 current = os.fsdecode(_git(root,
"config",
"--local",
"--get",
"core.hooksPath")).strip()
296 if current != str(unmanaged):
297 _fail(
"failed unmanaged install changed core.hooksPath")
298 _git(root,
"config",
"--local",
"core.hooksPath", str(managed))
299 intruder = managed /
"unmanaged"
300 _write(intruder,
"unmanaged\n")
301 result = _run_installer(root)
302 if result.returncode == 0:
303 _fail(
"installer replaced an unknown file in its managed directory")
305 hidden = managed /
".pre-commit.new.interrupted"
306 _write(hidden,
"interrupted\n")
307 result = _run_installer(root)
308 if result.returncode == 0:
309 _fail(
"installer ignored an interrupted hidden candidate")
313def _same_commit_bootstrap_case(base: Path) ->
None:
314 root = base /
"bootstrap"
316 _seed_launcher_repo(root, include_launcher=
False)
317 shutil.copy2(LAUNCHER, root /
"scripts/git/hook-launcher")
318 result = _run_installer(root)
319 if result.returncode == 0
or "HEAD does not own" not in result.stderr:
320 _fail(
"same-commit launcher bootstrap did not fail closed before commit")
321 _git(root,
"add",
"scripts/git/hook-launcher")
322 _git(root,
"commit",
"--quiet",
"-m",
"add launcher")
323 result = _run_installer(root)
324 if result.returncode:
325 _fail(f
"committed launcher did not install: {result.stderr}")
328def _wait_for_staging(common: Path, process: subprocess.Popen[str]) ->
None:
329 """Stop only after the install transaction owns a populated staging dir."""
330 deadline = time.monotonic() + 10
331 while time.monotonic() < deadline:
332 if tuple(common.glob(
"ra8-hooks.stage.*")):
334 if process.poll()
is not None:
335 _fail(f
"installer exited before staging: {process.returncode}")
337 _fail(
"installer did not expose its staging transaction")
340def _installer_transaction_case(base: Path) ->
None:
341 root = base /
"installer-transaction"
343 _seed_launcher_repo(root)
344 result = _run_installer(root)
345 if result.returncode:
346 _fail(f
"initial transactional install failed: {result.stderr}")
347 launcher = root /
"scripts/git/hook-launcher"
348 with launcher.open(
"ab")
as stream:
349 stream.write(b
"\n# padding keeps the transaction observable\n")
350 stream.write(os.urandom(8 * 1024 * 1024))
351 _git(root,
"add",
"scripts/git/hook-launcher")
352 _git(root,
"commit",
"--quiet",
"-m",
"large launcher transaction fixture")
353 environment = sanitized_git_environment()
354 common = _managed_dir(root).parent
355 process = subprocess.Popen(
356 default_signal_test_command(
"/bin/bash",
"-p", str(INSTALLER)),
359 stdout=subprocess.PIPE,
360 stderr=subprocess.PIPE,
362 start_new_session=
True,
365 _wait_for_staging(common, process)
366 os.kill(process.pid, signal.SIGSTOP)
367 contender = _run_installer(root)
368 if contender.returncode == 0
or "another hook installer" not in contender.stderr:
369 _fail(
"concurrent installer did not fail on the common-dir lock")
370 os.kill(process.pid, signal.SIGCONT)
371 os.killpg(process.pid, signal.SIGTERM)
372 process.communicate(timeout=15)
374 if process.poll()
is None:
375 with suppress(ProcessLookupError):
376 os.killpg(process.pid, signal.SIGKILL)
377 process.wait(timeout=5)
378 residue = tuple(common.glob(
"ra8-hooks.*"))
379 hooks = tuple(sorted(path.name
for path
in _managed_dir(root).iterdir()))
380 if residue
or hooks != tuple(sorted(HOOK_NAMES)):
381 _fail(
"interrupted installer left residue or a partial hook generation")
384ManagedState = tuple[int, tuple[tuple[str, int, bytes], ...]] |
None
385ConfigState = tuple[bool, str]
388def _managed_state(root: Path) -> ManagedState:
389 """Return the installed generation's exact names, modes, and bytes."""
390 managed = _managed_dir(root)
391 if not managed.exists():
394 (path.name, path.stat().st_mode & 0o777, path.read_bytes())
395 for path
in sorted(managed.iterdir())
397 return managed.stat().st_mode & 0o777, entries
400def _committed_state(root: Path) -> ManagedState:
401 """Return the only generation a successful transaction may install."""
402 launcher = _git(root,
"show",
"HEAD:scripts/git/hook-launcher")
403 return 0o700, tuple((name, 0o500, launcher)
for name
in HOOK_NAMES)
406def _hooks_config_state(root: Path) -> ConfigState:
407 """Distinguish an absent key from a present empty or nonempty value."""
408 result = _git_result(root,
"config",
"--local",
"--get",
"core.hooksPath")
409 if result.returncode == 0:
410 return True, result.stdout.decode(encoding=
"utf-8").removesuffix(
"\n")
411 if result.returncode == 1:
413 _fail(f
"fatal hooksPath read in fixture: {result.stderr!r}")
417def _configure_fixture(root: Path, state: str) -> tuple[ConfigState, ManagedState]:
418 """Prepare one exact pre-transaction config/directory state."""
419 if state ==
"managed":
420 result = _run_installer(root)
421 if result.returncode:
422 _fail(f
"managed fixture install failed: {result.stderr}")
423 elif state ==
"empty":
424 _git(root,
"config",
"--local",
"core.hooksPath",
"")
425 elif state ==
"legacy":
426 _git(root,
"config",
"--local",
"core.hooksPath",
"scripts/git")
427 elif state !=
"absent":
428 _fail(f
"unknown hook config fixture: {state}")
429 return _hooks_config_state(root), _managed_state(root)
432def _signal_injected_installer(
435 insertions: tuple[tuple[str, str], ...],
437 """Materialize the real installer with deterministic signal injections."""
438 text = INSTALLER.read_text(encoding=
"ascii")
439 for needle, injected
in insertions:
440 if text.count(needle) != 1:
441 _fail(f
"installer signal boundary {label} is not unique: {needle!r}")
442 text = text.replace(needle, f
"{needle}\n{injected}", 1)
443 path = base / f
"install-hooks-{label}.sh"
444 _write(path, text, executable=
True)
448def _assert_transaction_state(
452 generation: ManagedState,
454 """Require exact configuration presence/value and launcher bytes/modes."""
455 if _hooks_config_state(root) != config:
456 _fail(f
"{label}: transaction changed exact core.hooksPath state")
457 if _managed_state(root) != generation:
458 _fail(f
"{label}: transaction produced incorrect launcher bytes or modes")
461@dataclass(frozen=True)
463 """One exact signal/configuration transaction interruption."""
468 config_state: str =
"managed"
469 signal_name: str =
"TERM"
470 second_cleanup_signal: bool =
False
473def _installer_boundary_signal_case(base: Path, case: _BoundaryCase) ->
None:
474 """Signal one exact transaction boundary and verify the resulting generation."""
475 root = base / f
"installer-boundary-{case.label}"
477 _seed_launcher_repo(root)
478 original_config, original_state = _configure_fixture(root, case.config_state)
479 launcher = root /
"scripts/git/hook-launcher"
480 with launcher.open(
"a", encoding=
"ascii")
as stream:
481 stream.write(
"\n# boundary signal generation\n")
482 _git(root,
"add",
"scripts/git/hook-launcher")
483 _git(root,
"commit",
"--quiet",
"-m",
"new launcher generation")
485 committed_state = _committed_state(root)
486 insertions = [(case.boundary, f
' kill -{case.signal_name} "$$"')]
487 if case.second_cleanup_signal:
488 insertions.append((
" trap '' HUP INT QUIT TERM",
' kill -TERM "$$"'))
489 installer = _signal_injected_installer(base, case.label, tuple(insertions))
490 result = _run_installer(root, installer=installer)
491 if result.returncode != ABORTED:
492 _fail(f
"{case.label}: injected termination returned {result.returncode}, not 3")
494 expected_config = (
True, str(_managed_dir(root)))
495 _assert_transaction_state(root, case.label, expected_config, committed_state)
497 _assert_transaction_state(root, case.label, original_config, original_state)
498 residue = tuple(_managed_dir(root).parent.glob(
"ra8-hooks.*"))
500 _fail(f
"{case.label}: interrupted transaction left residue: {residue!r}")
503def _installer_boundary_signal_cases(base: Path) ->
None:
504 """Inject termination after every directory/configuration commit boundary."""
506 (
"before-first-move",
" transaction_started=1",
False,
False),
507 (
"after-backup-move",
' mv -- "$managed" "$backup"',
False,
False),
508 (
"after-install-move",
' mv -- "$staging" "$managed"',
False,
False),
511 ' "$TRUSTED_GIT" -C "$root" config --local core.hooksPath "$managed"',
515 (
"after-commit-record",
" installed=1",
True,
False),
516 (
"rollback-second-signal",
' mv -- "$managed" "$backup"',
False,
True),
518 for label, boundary, committed, second_cleanup_signal
in cases:
519 _installer_boundary_signal_case(
521 _BoundaryCase(label, boundary, committed, second_cleanup_signal=second_cleanup_signal),
523 for config_state
in (
"absent",
"empty",
"legacy",
"managed"):
524 for signal_name
in (
"HUP",
"INT",
"QUIT",
"TERM"):
525 label = f
"matrix-{config_state}-{signal_name.lower()}"
526 _installer_boundary_signal_case(
530 ' "$TRUSTED_GIT" -C "$root" config --local core.hooksPath "$managed"',
532 config_state=config_state,
533 signal_name=signal_name,
536 _installer_restore_failure_case(base)
537 _installer_config_read_failure_case(base)
538 _installer_multiline_config_case(base)
539 _installer_exact_generation_case(base)
540 _installer_lock_acquisition_signal_case(base)
543def _installer_lock_acquisition_signal_case(base: Path) ->
None:
544 """Defer termination until an acquired installer lock can be released."""
545 root = base /
"installer-lock-acquisition-signal"
547 _seed_launcher_repo(root)
548 installer = _signal_injected_installer(
550 "lock-acquisition-signal",
551 ((
' if mkdir -- "$lock" 2>/dev/null; then',
' kill -TERM "$$"'),),
553 result = _run_installer(root, installer=installer)
554 if result.returncode != ABORTED:
555 _fail(f
"lock acquisition termination returned {result.returncode}, not 3")
556 _assert_transaction_state(root,
"lock-acquisition-signal", (
False,
""),
None)
557 residue = tuple((root /
".git").glob(
"ra8-hooks.*"))
559 _fail(f
"lock acquisition termination left residue: {residue!r}")
560 retry = _run_installer(root)
562 _fail(f
"lock acquisition termination blocked retry: {retry.stderr}")
563 expected_config = (
True, str(_managed_dir(root)))
564 _assert_transaction_state(
566 "lock-acquisition-retry",
568 _committed_state(root),
572def _installer_restore_failure_case(base: Path) ->
None:
573 """Retain an exact recovery backup when directory restoration fails."""
574 root = base /
"installer-restore-failure"
576 _seed_launcher_repo(root)
577 original_config, original_state = _configure_fixture(root,
"managed")
578 launcher = root /
"scripts/git/hook-launcher"
579 with launcher.open(
"a", encoding=
"ascii")
as stream:
580 stream.write(
"\n# restore failure generation\n")
581 _git(root,
"add",
"scripts/git/hook-launcher")
582 _git(root,
"commit",
"--quiet",
"-m",
"new launcher generation")
583 text = INSTALLER.read_text(encoding=
"ascii")
584 move =
' mv -- "$managed" "$backup"'
585 restore =
' if mv -- "$backup" "$managed"; then'
586 if text.count(move) != 1
or text.count(restore) != 1:
587 _fail(
"restore-failure fixture did not bind both production moves")
588 text = text.replace(move, f
'{move}\n kill -TERM "$$"', 1)
589 text = text.replace(restore,
" if false; then", 1)
590 installer = base /
"install-hooks-restore-failure.sh"
591 _write(installer, text, executable=
True)
592 result = _run_installer(root, installer=installer)
593 if result.returncode != 1
or "recovery backup retained" not in result.stderr:
594 _fail(
"restore failure did not return 1 and disclose retained recovery state")
595 if _hooks_config_state(root) != original_config
or _managed_state(root)
is not None:
596 _fail(
"restore failure changed configuration or fabricated a generation")
597 common = _managed_dir(root).parent
598 backups = tuple(common.glob(
"ra8-hooks.backup.*"))
599 if len(backups) != 1:
600 _fail(
"restore failure did not retain exactly one recovery backup")
603 backup.stat().st_mode & 0o777,
605 (path.name, path.stat().st_mode & 0o777, path.read_bytes())
606 for path
in sorted(backup.iterdir())
609 if backup_state != original_state:
610 _fail(
"retained recovery backup changed original bytes or modes")
613def _installer_config_read_failure_case(base: Path) ->
None:
614 """Require a fatal hooksPath read to abort before any transaction."""
615 root = base /
"installer-config-read-failure"
617 _seed_launcher_repo(root)
618 text = INSTALLER.read_text(encoding=
"ascii")
619 needle =
' read_hooks_path() {\n local count=0 row status=""'
620 if text.count(needle) != 1:
621 _fail(
"fatal config-read fixture did not bind the production helper")
624 ' read_hooks_path() {\n return 5\n local count=0 row status=""',
627 installer = base /
"install-hooks-config-read-failure.sh"
628 _write(installer, text, executable=
True)
629 result = _run_installer(root, installer=installer)
630 if result.returncode != 1
or "cannot read local core.hooksPath" not in result.stderr:
631 _fail(
"fatal hooksPath read did not fail closed")
632 if _managed_state(root)
is not None or tuple((root /
".git").glob(
"ra8-hooks.*")):
633 _fail(
"fatal hooksPath read started a transaction")
636def _installer_multiline_config_case(base: Path) ->
None:
637 """Refuse an unmanaged trailing-newline value without normalizing it."""
638 root = base /
"installer-multiline-config"
640 _seed_launcher_repo(root)
641 value =
"scripts/git\n"
642 _git(root,
"config",
"--local",
"core.hooksPath", value)
643 before = _hooks_config_state(root)
644 result = _run_installer(root)
645 if result.returncode != 1
or "refusing to replace unmanaged" not in result.stderr:
646 _fail(
"multiline hooksPath was normalized into an allowed value")
647 if before != (
True, value)
or _hooks_config_state(root) != before:
648 _fail(
"multiline hooksPath did not preserve its exact value")
649 if _managed_state(root)
is not None:
650 _fail(
"multiline hooksPath refusal installed a managed generation")
653def _installer_exact_generation_case(base: Path) ->
None:
654 """Repair unauthorized installed bytes to the exact committed generation."""
655 root = base /
"installer-exact-generation"
657 _seed_launcher_repo(root)
658 first = _run_installer(root)
660 _fail(f
"exact generation fixture failed to install: {first.stderr}")
661 managed = _managed_dir(root)
662 mutated = managed /
"pre-commit"
664 with mutated.open(
"ab")
as stream:
665 stream.write(b
"# unauthorized installed bytes\n")
666 (managed /
"pre-push").chmod(0o700)
667 second = _run_installer(root)
668 if second.returncode:
669 _fail(f
"installer did not repair unauthorized installed generation: {second.stderr}")
670 expected_config = (
True, str(managed))
671 _assert_transaction_state(root,
"exact-generation", expected_config, _committed_state(root))
674def _launcher_signal_case(base: Path) ->
None:
675 root = base /
"launcher-signal"
676 ready = base /
"launcher.ready"
677 continued = base /
"launcher.continued"
679 _seed_launcher_repo(root)
680 script =
"""#!/usr/bin/env bash
682trap 'exit 3' HUP INT QUIT TERM
683printf 'ready\\n' >"${RA8_SELFTEST_READY:?}"
685printf 'continued\\n' >"${RA8_SELFTEST_CONTINUED:?}"
687 _write(root /
"scripts/git/pre-commit", script, executable=
True)
688 _git(root,
"add",
"scripts/git/pre-commit")
689 _git(root,
"commit",
"--quiet",
"-m",
"signal owner")
690 result = _run_installer(root)
691 if result.returncode:
692 _fail(f
"signal fixture install failed: {result.stderr}")
693 environment = sanitized_git_environment()
694 environment.update(RA8_SELFTEST_READY=str(ready), RA8_SELFTEST_CONTINUED=str(continued))
695 proc = subprocess.Popen(
696 default_signal_test_command(str(_managed_dir(root) /
"pre-commit")),
699 stdout=subprocess.PIPE,
700 stderr=subprocess.PIPE,
701 start_new_session=
True,
704 _wait_path(ready, proc)
705 os.kill(proc.pid, signal.SIGTERM)
706 proc.communicate(timeout=15)
708 if proc.poll()
is None:
709 with suppress(ProcessLookupError):
710 os.killpg(proc.pid, signal.SIGKILL)
712 residue = tuple(_managed_dir(root).parent.glob(
"ra8-hook-run.*"))
713 if proc.returncode != ABORTED
or continued.exists()
or residue:
714 _fail(
"launcher did not preserve owner semantics after parent-only SIGTERM")
717def _wait_path(path: Path, proc: subprocess.Popen[bytes]) ->
None:
718 deadline = time.monotonic() + 10
719 while time.monotonic() < deadline:
722 if proc.poll()
is not None:
723 _fail(f
"fixture exited before ready: {proc.returncode}")
725 _fail(
"fixture did not report readiness")
728def _extract_supervisor() -> str:
729 text = PRE_COMMIT.read_text(encoding=
"utf-8")
730 start = text.index(
"<<'PY' || true\n") + len(
"<<'PY' || true\n")
731 end = text.index(
"\nPY\n", start)
732 return text[start:end]
735def _supervisor_failure_case(base: Path, mode: str) ->
None:
736 ready = base / f
"{mode}.ready"
737 child_pid = base / f
"{mode}.pid"
738 child = base / f
"{mode}.sh"
739 _write(child, f
"#!/usr/bin/env bash\necho $$ >{child_pid!s}\nsleep 60\n", executable=
True)
740 if mode ==
"collision":
741 _write(ready,
"occupied\n")
742 environment = sanitized_git_environment()
743 if mode ==
"write-failure":
744 ready = base /
"missing-ready-parent" /
"ready"
749 _extract_supervisor(),
755 result = subprocess.run(
756 command, env=environment, capture_output=
True, check=
False, timeout=15
758 if result.returncode == 0:
759 _fail(f
"supervisor {mode} returned success")
760 if child_pid.exists():
761 pgid = int(child_pid.read_text(encoding=
"ascii").strip())
764 except ProcessLookupError:
767 with suppress(ProcessLookupError):
768 os.killpg(pgid, signal.SIGKILL)
769 _fail(f
"supervisor {mode} left its child process group alive")
772def _supervisor_failure_cases(base: Path) ->
None:
773 _supervisor_failure_case(base,
"collision")
774 _supervisor_failure_case(base,
"write-failure")
775 result = subprocess.run(
780 _extract_supervisor(),
781 str(base /
"missing.ready"),
784 env=sanitized_git_environment(),
789 if result.returncode == 0:
790 _fail(
"supervisor interpreter failure returned success")
793def _stub_ci_support(root: Path) ->
None:
794 for relative
in (
"git_environment.sh",):
795 _write(root / f
"scripts/dev/{relative}",
"#!/usr/bin/env bash\n")
796 _write(root /
"scripts/ci/lib/parallelism.sh",
"#!/usr/bin/env bash\n")
797 _write(root /
"scripts/ci/lib/arm_toolchain.sh",
"#!/usr/bin/env bash\n")
798 _write(root /
"scripts/ci/lib/snapshot.sh",
"#!/usr/bin/env bash\n")
799 _write(root /
"scripts/ci/lib/tool_env.sh",
"use_pinned_tool_path() { :; }\n")
801 root /
"scripts/ci/lib/abort.sh",
802 "RA8_CI_EXIT_ABORTED=3\nci_require_tree_intact() { :; }\nci_install_abort_traps() { :; }\n",
804 gate_source =
"""for row in "${RA8_GATE_REGISTRY[@]}"; do
806 fn="gate_${name//-/_}"
807 eval "$fn() { \"$RA8_SELFTEST_IGNORED_PROBE\"; \\
808 printf '%s\\n' '$name' >>\"$RA8_SELFTEST_GATE_LOG\"; }"
811 _write(root /
"scripts/ci/gates/fixture.sh", gate_source)
814def _policy_environment(root: Path) -> tuple[Path, dict[str, str]]:
815 gate_log = root /
".git/gates.log"
816 probe = root /
".venv/bin/probe"
819 "#!/usr/bin/env bash\n"
820 "if env | grep -Eq '^RA8_STAGED_(HOOK|GATE)_PROOF'; then exit 91; fi\n",
823 environment = sanitized_git_environment()
824 tools = root /
".git/policy-tools"
826 (tools /
"git").symlink_to(trusted_git_executable())
827 (tools /
"bash").symlink_to(
"/bin/bash")
829 PATH=f
"{tools}:{environment.get('PATH', os.defpath)}",
830 RA8_STAGED_HOOK_SNAPSHOT=
"1",
831 RA8_SELFTEST_GATE_LOG=str(gate_log),
832 RA8_SELFTEST_IGNORED_PROBE=str(probe),
833 RA8_TOOLS_CACHE=str(root /
".git/tool-cache"),
835 return gate_log, environment
838def _policy_fixture(root: Path, hooks_text: str) -> tuple[Path, dict[str, str]]:
840 _write(root /
"just/hooks.just", hooks_text)
844 'set shell := ["/bin/bash", "-puc"]\n'
845 'export BASH_ENV := "/dev/null"\n'
846 'export ENV := "/dev/null"\n'
847 'export PYTHONHOME := ""\n'
848 'export PYTHONPATH := ""\n'
849 'mod git_hooks "just/hooks.just"\n'
850 'mod quality "quality.just"\n'
853 _write(root /
"quality.just",
'set working-directory := "."\nmod local "quality_local.just"\n')
855 root /
"quality_local.just",
856 'set working-directory := "."\ngate name:\n'
857 ' /bin/bash -p scripts/ci.sh --gate "{{ name }}"\n',
859 (root /
"scripts/git").mkdir(parents=
True)
860 shutil.copy2(CI_SCRIPT, root /
"scripts/ci.sh")
861 shutil.copy2(PROOF_WRITER, root /
"scripts/git/write-proof.py")
862 _stub_ci_support(root)
864 "check_hook_parity.py",
865 "check_mcdc_block.py",
866 "check_new_compound_has_mcdc.py",
867 "check_obsolete_standards.py",
870 root / f
"scripts/checks/{name}",
871 "#!/usr/bin/env python3\nraise SystemExit(0)\n",
874 _write(root /
"sample.c",
"int value;\n")
875 _write(root /
".gitignore",
".venv/\n")
876 _git(root,
"add",
".")
877 _git(root,
"commit",
"--quiet",
"-m",
"fixture")
878 _write(root /
"sample.c",
"int value = 1;\n")
879 _git(root,
"add",
"sample.c")
880 return _policy_environment(root)
883def _policy_case(base: Path, name: str, hooks_text: str) -> bool:
886 gate_log, environment = _policy_fixture(root, hooks_text)
887 just = shutil.which(
"just")
889 _fail(
"Just is required for the real recipe runtime selftest")
890 result = subprocess.run(
895 "--clear-shell-args",
899 str(root /
"justfile"),
900 "--working-directory",
902 "git_hooks::pre-commit",
911 gates = tuple(gate_log.read_text(encoding=
"utf-8").splitlines())
if gate_log.exists()
else ()
912 return result.returncode == 0
and gates == EXPECTED_GATES
915def _real_policy_cases(base: Path) ->
None:
916 live = HOOKS_JUST.read_text(encoding=
"utf-8")
917 if not _policy_case(base,
"policy-live", live):
918 _fail(
"real Just pre-commit recipe did not run every gate")
919 early = live.replace(
" gates=(",
" exit 0\n gates=(", 1)
921 ' for gate in "${gates[@]}"; do\n run_gate "$gate"\n done',
923 ' if false; then\n for gate in "${gates[@]}"; do\n'
924 ' run_gate "$gate"\n done\n fi'
928 reordered = live.replace(
929 " ascii\n copyright",
" copyright\n ascii", 1
931 for name, mutated
in (
932 (
"policy-early", early),
933 (
"policy-dead", dead),
934 (
"policy-order", reordered),
936 if _policy_case(base, name, mutated):
937 _fail(f
"real Just runtime accepted {name} mutation")
940def _proof_writer_case(base: Path) ->
None:
941 proof = base /
"atomic.proof"
942 command = [
"/usr/bin/python3",
"-I", str(PROOF_WRITER), str(proof)]
943 first = subprocess.run(
944 command, input=b
"token\n", capture_output=
True, check=
False
946 second = subprocess.run(
947 command, input=b
"token\n", capture_output=
True, check=
False
949 target = base /
"target"
950 target.write_text(
"unchanged\n", encoding=
"ascii")
951 linked = base /
"linked.proof"
952 linked.symlink_to(target)
953 linked_result = subprocess.run(
954 [
"/usr/bin/python3",
"-I", str(PROOF_WRITER), str(linked)],
959 if first.returncode
or second.returncode == 0
or linked_result.returncode == 0:
960 _fail(
"atomic proof writer did not enforce exclusive no-follow creation")
961 if target.read_text(encoding=
"ascii") !=
"unchanged\n":
962 _fail(
"atomic proof writer followed a final-component symlink")
965def run_runtime_selftests() -> None:
966 """Run all hook ownership, supervisor, real-policy, and proof fixtures."""
967 with tempfile.TemporaryDirectory(prefix=
"ra8-hook-runtime-")
as temporary:
968 base = Path(temporary)
969 _launcher_immutability_case(base)
970 _installer_path_case(base)
971 _same_commit_bootstrap_case(base)
972 _launcher_signal_case(base)
973 _supervisor_failure_cases(base)
974 _installer_transaction_case(base)
975 _installer_boundary_signal_cases(base)
976 _real_policy_cases(base)
977 _proof_writer_case(base)