From d227cfcb101ef6017e3597b210add9d744ea364f Mon Sep 17 00:00:00 2001 From: rcourtman <8825017+rcourtman@users.noreply.github.com> Date: Wed, 2 Sep 2026 09:55:47 +0100 Subject: [PATCH] Stop background git from racing test_docs_mirror temp cleanup test_staged_root_sourced_doc_with_stale_mirror_fails errored in the "Script smoke tests & backend build" job on PR #1857 (run 33609703555) with OSError [Errno 39] Directory not empty: '.git' raised from TemporaryDirectory cleanup. The test body passed; a background git process spawned by init/add/commit (auto-gc, fsmonitor, or maintenance) was still writing under .git when shutil.rmtree ran. The suite passes on main most of the time and locally, so this is a race, not a logic bug. Disable gc.auto, core.fsmonitor and maintenance.auto for the throwaway repos, and point GIT_CONFIG_GLOBAL/GIT_CONFIG_SYSTEM at os.devnull so a runner's host config cannot re-enable them. Construct both temp directories with ignore_cleanup_errors=True as a belt-and-braces fallback so a straggler can never fail a test whose assertions already passed. --- scripts/tests/test_docs_mirror.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/scripts/tests/test_docs_mirror.py b/scripts/tests/test_docs_mirror.py index 8376ad149..7a149f68c 100644 --- a/scripts/tests/test_docs_mirror.py +++ b/scripts/tests/test_docs_mirror.py @@ -4,6 +4,7 @@ from __future__ import annotations import importlib.util +import os from pathlib import Path import subprocess import sys @@ -20,6 +21,17 @@ sys.modules[SPEC.name] = docs_mirror SPEC.loader.exec_module(docs_mirror) +# Background git processes (auto-gc, fsmonitor, maintenance) can still be +# writing under .git when TemporaryDirectory cleanup runs, which makes rmtree +# fail with "Directory not empty". Disable them for the throwaway repos and +# ignore host config so it cannot re-enable them. +GIT_ENV = { + **os.environ, + "GIT_CONFIG_GLOBAL": os.devnull, + "GIT_CONFIG_SYSTEM": os.devnull, +} + + def run_git(root: Path, *args: str) -> None: subprocess.run( [ @@ -30,10 +42,17 @@ def run_git(root: Path, *args: str) -> None: "user.email=test@example.invalid", "-c", "user.name=test", + "-c", + "gc.auto=0", + "-c", + "core.fsmonitor=false", + "-c", + "maintenance.auto=false", *args, ], check=True, capture_output=True, + env=GIT_ENV, ) @@ -59,7 +78,7 @@ class DocsMirrorMappingTest(unittest.TestCase): class DocsMirrorStagedTest(unittest.TestCase): def setUp(self) -> None: - self._temporary = tempfile.TemporaryDirectory() + self._temporary = tempfile.TemporaryDirectory(ignore_cleanup_errors=True) self.addCleanup(self._temporary.cleanup) self.root = Path(self._temporary.name) run_git(self.root, "init", "-q") @@ -139,7 +158,7 @@ class DocsMirrorStagedTest(unittest.TestCase): class DocsMirrorWorktreeTest(unittest.TestCase): def test_worktree_drift_and_sync(self) -> None: - with tempfile.TemporaryDirectory() as temporary: + with tempfile.TemporaryDirectory(ignore_cleanup_errors=True) as temporary: root = Path(temporary) write(root, "docs/GUIDE.md", "# Guide v2\n") write(root, "frontend-modern/public/docs/GUIDE.md", "# Guide v1\n")