diff --git a/tests/git_fixture_env.py b/tests/git_fixture_env.py new file mode 100644 index 000000000000..4f5d9a032b83 --- /dev/null +++ b/tests/git_fixture_env.py @@ -0,0 +1,30 @@ +"""Keep git from leaving a background writer in temporary fixture repositories. + +git commit, fetch, and clone start `git maintenance run --auto --detach`, +which can outlive the command and still be writing into `.git` when a +TemporaryDirectory is removed ("Directory not empty: '.git'"). Importing this +module turns that off for every git child of the importing test process; +tests that build a git environment from scratch pass it through +`without_auto_maintenance`. +""" + +from __future__ import annotations + +import os +from collections.abc import MutableMapping + +_KEY = "maintenance.auto" + + +def without_auto_maintenance(env: MutableMapping[str, str]) -> MutableMapping[str, str]: + """Append maintenance.auto=false to env's GIT_CONFIG_* pairs, once.""" + count = int(env.get("GIT_CONFIG_COUNT") or 0) + if any(env.get(f"GIT_CONFIG_KEY_{index}") == _KEY for index in range(count)): + return env + env[f"GIT_CONFIG_KEY_{count}"] = _KEY + env[f"GIT_CONFIG_VALUE_{count}"] = "false" + env["GIT_CONFIG_COUNT"] = str(count + 1) + return env + + +without_auto_maintenance(os.environ) diff --git a/tests/test_app_host_test_rerun.py b/tests/test_app_host_test_rerun.py index e309bd1ad2a1..604100a8eb41 100644 --- a/tests/test_app_host_test_rerun.py +++ b/tests/test_app_host_test_rerun.py @@ -17,6 +17,7 @@ sys.path.insert(0, str(ROOT / "scripts" / "ci")) import app_host_test_rerun as rerun # noqa: E402 +import git_fixture_env # noqa: F401 (disables git auto maintenance) WORKFLOW = ROOT / ".github" / "workflows" / "app-host-test-rerun.yml" diff --git a/tests/test_benchmark_dev_fleet_warm_slots.py b/tests/test_benchmark_dev_fleet_warm_slots.py index a915c6d72af0..e06261872ba1 100644 --- a/tests/test_benchmark_dev_fleet_warm_slots.py +++ b/tests/test_benchmark_dev_fleet_warm_slots.py @@ -8,6 +8,7 @@ import tempfile import unittest from unittest import mock +import git_fixture_env # noqa: F401 (disables git auto maintenance) ROOT = Path(__file__).resolve().parents[1] BENCH = ROOT / "scripts" / "benchmark-dev-fleet-warm-slots.py" diff --git a/tests/test_check_package_resolved_policy.py b/tests/test_check_package_resolved_policy.py index 2bbed05b66c5..b9028674e364 100644 --- a/tests/test_check_package_resolved_policy.py +++ b/tests/test_check_package_resolved_policy.py @@ -27,6 +27,7 @@ import subprocess import sys import tempfile +import git_fixture_env # noqa: F401 (disables git auto maintenance) ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) GUARD = os.path.join(ROOT_DIR, "scripts", "check-package-resolved-policy.py") diff --git a/tests/test_ci_change_areas.py b/tests/test_ci_change_areas.py index 77e3981767d9..79bdf0175a32 100755 --- a/tests/test_ci_change_areas.py +++ b/tests/test_ci_change_areas.py @@ -19,23 +19,7 @@ import yaml - -def _disable_git_auto_maintenance() -> None: - """Stop git from leaving a background writer in fixture repositories. - - git commit, fetch, and clone start `git maintenance run --auto --detach`, - which can outlive the command and still be writing into `.git` when a - TemporaryDirectory is removed ("Directory not empty: '.git'"). Every git - child of this module, including the ones the routed workflow scripts - start, inherits this environment. - """ - index = int(os.environ.get("GIT_CONFIG_COUNT") or 0) - os.environ[f"GIT_CONFIG_KEY_{index}"] = "maintenance.auto" - os.environ[f"GIT_CONFIG_VALUE_{index}"] = "false" - os.environ["GIT_CONFIG_COUNT"] = str(index + 1) - - -_disable_git_auto_maintenance() +import git_fixture_env # noqa: F401 (disables git auto maintenance) ROOT = Path(__file__).resolve().parents[1] HELPER = ROOT / "scripts" / "ci" / "detect_ci_change_areas.py" diff --git a/tests/test_ci_fetch_complexity_base.py b/tests/test_ci_fetch_complexity_base.py index 4a2fc32101d0..979ee467b6d4 100644 --- a/tests/test_ci_fetch_complexity_base.py +++ b/tests/test_ci_fetch_complexity_base.py @@ -5,6 +5,7 @@ import subprocess import tempfile import unittest +import git_fixture_env # noqa: F401 (disables git auto maintenance) ROOT = Path(__file__).resolve().parents[1] HELPER = ROOT / "scripts/ci/fetch-complexity-base.sh" diff --git a/tests/test_ci_git_seed.py b/tests/test_ci_git_seed.py index 76c48dbc3420..17b686c0eb44 100644 --- a/tests/test_ci_git_seed.py +++ b/tests/test_ci_git_seed.py @@ -10,6 +10,7 @@ import tempfile import unittest from pathlib import Path +import git_fixture_env ROOT = Path(__file__).resolve().parents[1] SCRIPT = ROOT / "scripts" / "ci" / "git-seed.sh" @@ -46,12 +47,12 @@ def setUp(self): self.module_v1 = commit(self.module, "m.txt", "module v1") git("submodule", "add", "-q", f"file://{self.module}", "vendor/module", cwd=self.super) self.main = commit(self.super, "app.txt", "main") - self.env = dict( + self.env = git_fixture_env.without_auto_maintenance(dict( os.environ, GITHUB_SERVER_URL=f"file://{self.server}", GITHUB_REPOSITORY="acme/super", GIT_CONFIG_COUNT="1", GIT_CONFIG_KEY_0="protocol.file.allow", GIT_CONFIG_VALUE_0="always", - ) + )) def seed_from_main(self) -> Path: """What the main-branch seeder checks out and stages.""" diff --git a/tests/test_ci_ios_conventions_diff.py b/tests/test_ci_ios_conventions_diff.py index f0e9c68cdcb5..1bea237bb3f0 100755 --- a/tests/test_ci_ios_conventions_diff.py +++ b/tests/test_ci_ios_conventions_diff.py @@ -12,6 +12,7 @@ import tempfile import unittest from pathlib import Path +import git_fixture_env # noqa: F401 (disables git auto maintenance) ROOT = Path(__file__).resolve().parents[1] GATE = ROOT / "scripts/ci/lint-ios-conventions-diff.sh" diff --git a/tests/test_ci_main_regression_attribution.py b/tests/test_ci_main_regression_attribution.py index 8ac099dd42d4..a911cfd5457d 100644 --- a/tests/test_ci_main_regression_attribution.py +++ b/tests/test_ci_main_regression_attribution.py @@ -5,6 +5,7 @@ import pathlib import sys import unittest +import git_fixture_env # noqa: F401 (disables git auto maintenance) ROOT = pathlib.Path(__file__).resolve().parents[1] SCRIPT = ROOT / "scripts/ci/main_regression_attribution.py" diff --git a/tests/test_ci_owned_build_state.py b/tests/test_ci_owned_build_state.py index 0a67a213ae10..13473244dc61 100644 --- a/tests/test_ci_owned_build_state.py +++ b/tests/test_ci_owned_build_state.py @@ -19,6 +19,7 @@ sys.path.insert(0, str(ROOT / "scripts/ci")) import owned_build_state as state # noqa: E402 +import git_fixture_env # noqa: F401 (disables git auto maintenance) OWNED = "startsWith(env.CMUX_PRODUCT_RUNNER, 'glaeda-')" diff --git a/tests/test_ci_reload_build_cache_keys.py b/tests/test_ci_reload_build_cache_keys.py index d030d01d8b53..309e003e8036 100644 --- a/tests/test_ci_reload_build_cache_keys.py +++ b/tests/test_ci_reload_build_cache_keys.py @@ -27,6 +27,7 @@ from pathlib import Path import yaml +import git_fixture_env # noqa: F401 (disables git auto maintenance) ROOT = Path(__file__).resolve().parents[1] diff --git a/tests/test_ci_run_guards.py b/tests/test_ci_run_guards.py index dc75b07b905a..8a21d28063f0 100644 --- a/tests/test_ci_run_guards.py +++ b/tests/test_ci_run_guards.py @@ -23,6 +23,7 @@ sys.path.insert(0, str(ROOT / "scripts" / "ci")) import run_ci_guards # noqa: E402 +import git_fixture_env # noqa: F401 (disables git auto maintenance) FAST_WORKFLOW = ROOT / ".github/workflows/ci-fast-guards.yml" diff --git a/tests/test_ci_test_execution_registry.py b/tests/test_ci_test_execution_registry.py index cdb71496ae6e..0a3a72cbbf28 100644 --- a/tests/test_ci_test_execution_registry.py +++ b/tests/test_ci_test_execution_registry.py @@ -15,6 +15,7 @@ import tempfile import unittest from pathlib import Path +import git_fixture_env # noqa: F401 (disables git auto maintenance) ROOT = Path(__file__).resolve().parents[1] diff --git a/tests/test_ci_workload_profiles.py b/tests/test_ci_workload_profiles.py index 9490e9ed61d8..cfffdf8ba311 100644 --- a/tests/test_ci_workload_profiles.py +++ b/tests/test_ci_workload_profiles.py @@ -11,6 +11,7 @@ import time import unittest from unittest import mock +import git_fixture_env # noqa: F401 (disables git auto maintenance) ROOT = Path(__file__).resolve().parents[1] RUNNER = ROOT / "scripts/ci/cmux_workload_profile.py" diff --git a/tests/test_cli_contract_help.py b/tests/test_cli_contract_help.py index 0fb06b24d1c4..972220187ae7 100644 --- a/tests/test_cli_contract_help.py +++ b/tests/test_cli_contract_help.py @@ -20,6 +20,8 @@ from dataclasses import dataclass from pathlib import Path +import git_fixture_env + START_MARKER = "" END_MARKER = "" @@ -138,6 +140,7 @@ def clean_git_env() -> dict[str, str]: for key in list(env): if key.startswith(("GIT_CONFIG_KEY_", "GIT_CONFIG_VALUE_")): env.pop(key) + git_fixture_env.without_auto_maintenance(env) return env diff --git a/tests/test_cmux_cua_build_cache_safety.py b/tests/test_cmux_cua_build_cache_safety.py index 67bac309b359..2303955a006b 100644 --- a/tests/test_cmux_cua_build_cache_safety.py +++ b/tests/test_cmux_cua_build_cache_safety.py @@ -11,6 +11,7 @@ import tempfile import time from pathlib import Path +import git_fixture_env # noqa: F401 (disables git auto maintenance) ROOT = Path(__file__).resolve().parents[1] diff --git a/tests/test_codex_wrapper_hook_append.py b/tests/test_codex_wrapper_hook_append.py index 651cbd276164..434f49cafe9f 100644 --- a/tests/test_codex_wrapper_hook_append.py +++ b/tests/test_codex_wrapper_hook_append.py @@ -29,6 +29,8 @@ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from pathlib import Path +import git_fixture_env # noqa: F401 (disables git auto maintenance) + from claude_teams_test_utils import ( FOCUSED_SURFACE_ID, FOCUSED_WORKSPACE_ID, diff --git a/tests/test_dev_fleet_warm_slot.py b/tests/test_dev_fleet_warm_slot.py index 2f9fb73d02a1..4ba46046ae16 100644 --- a/tests/test_dev_fleet_warm_slot.py +++ b/tests/test_dev_fleet_warm_slot.py @@ -13,6 +13,7 @@ import time import unittest from unittest import mock +import git_fixture_env # noqa: F401 (disables git auto maintenance) ROOT = Path(__file__).resolve().parents[1] HELPER = ROOT / "scripts" / "dev-fleet-warm-slot.py" diff --git a/tests/test_ensure_ghosttykit_zig.py b/tests/test_ensure_ghosttykit_zig.py index ef1cfc164df3..e1b4f97fa3e6 100644 --- a/tests/test_ensure_ghosttykit_zig.py +++ b/tests/test_ensure_ghosttykit_zig.py @@ -9,6 +9,7 @@ import tarfile import tempfile import unittest +import git_fixture_env # noqa: F401 (disables git auto maintenance) ROOT = Path(__file__).resolve().parents[1] diff --git a/tests/test_install_git_hooks.py b/tests/test_install_git_hooks.py index 0cdea9f84c94..d3a9e4ca6e6a 100644 --- a/tests/test_install_git_hooks.py +++ b/tests/test_install_git_hooks.py @@ -11,6 +11,7 @@ import subprocess import tempfile import unittest +import git_fixture_env # noqa: F401 (disables git auto maintenance) SOURCE = Path(__file__).resolve().parents[1] diff --git a/tests/test_ios_appstore_lane_identity.py b/tests/test_ios_appstore_lane_identity.py index 4625f9e9b7bd..54fccc86e7d3 100644 --- a/tests/test_ios_appstore_lane_identity.py +++ b/tests/test_ios_appstore_lane_identity.py @@ -21,6 +21,8 @@ import zipfile from pathlib import Path +import git_fixture_env # noqa: F401 (disables git auto maintenance) + ROOT = Path(__file__).resolve().parents[1] TEAM_ID = "7WLXT3NR37" diff --git a/tests/test_ios_testflight_notes.py b/tests/test_ios_testflight_notes.py index 8a98617316b8..82810a8805c9 100644 --- a/tests/test_ios_testflight_notes.py +++ b/tests/test_ios_testflight_notes.py @@ -14,6 +14,7 @@ import subprocess import sys import tempfile +import git_fixture_env # noqa: F401 (disables git auto maintenance) REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) SCRIPT = os.path.join(REPO_ROOT, "ios", "scripts", "generate-testflight-notes.sh") diff --git a/tests/test_ios_upload_batching.py b/tests/test_ios_upload_batching.py index c3ff4a02a2e4..a9d9d5b0bbb1 100644 --- a/tests/test_ios_upload_batching.py +++ b/tests/test_ios_upload_batching.py @@ -20,6 +20,7 @@ from pathlib import Path import yaml +import git_fixture_env # noqa: F401 (disables git auto maintenance) ROOT = Path(__file__).resolve().parents[1] SCRIPT = ROOT / "scripts/ci/ios_upload_batch_decision.py" diff --git a/tests/test_ios_upload_lean_checkout.py b/tests/test_ios_upload_lean_checkout.py index 50a18e2a739d..fc817b19b0b0 100755 --- a/tests/test_ios_upload_lean_checkout.py +++ b/tests/test_ios_upload_lean_checkout.py @@ -25,6 +25,8 @@ import yaml +import git_fixture_env # noqa: F401 (disables git auto maintenance) + ROOT = Path(__file__).resolve().parents[1] WORKFLOWS = { "ios-testflight.yml": "upload", diff --git a/tests/test_lint_errno_in_test_assertions.py b/tests/test_lint_errno_in_test_assertions.py index 950afb1907b7..840293d01e52 100755 --- a/tests/test_lint_errno_in_test_assertions.py +++ b/tests/test_lint_errno_in_test_assertions.py @@ -16,6 +16,8 @@ import unittest from pathlib import Path +import git_fixture_env # noqa: F401 (disables git auto maintenance) + REPO_ROOT = Path(__file__).resolve().parent.parent MODULE_PATH = REPO_ROOT / "scripts" / "lint-errno-in-test-assertions.py" diff --git a/tests/test_localize_changes.py b/tests/test_localize_changes.py index c61e573eeed9..0c0b117879d2 100644 --- a/tests/test_localize_changes.py +++ b/tests/test_localize_changes.py @@ -11,6 +11,8 @@ from pathlib import Path from unittest.mock import patch +import git_fixture_env # noqa: F401 (disables git auto maintenance) + ROOT = Path(__file__).resolve().parents[1] SPEC = importlib.util.spec_from_file_location("localize_changes", ROOT / "scripts/localize_changes.py") MODULE = importlib.util.module_from_spec(SPEC) diff --git a/tests/test_node_product_cache.py b/tests/test_node_product_cache.py index 8de555cb5d4b..2a50c4dfc04a 100755 --- a/tests/test_node_product_cache.py +++ b/tests/test_node_product_cache.py @@ -15,6 +15,7 @@ import unittest from pathlib import Path from unittest import mock +import git_fixture_env # noqa: F401 (disables git auto maintenance) ROOT = Path(__file__).resolve().parents[1] MODULE = ROOT / "scripts/ci/node_product_cache.py" diff --git a/tests/test_preflight_trust.py b/tests/test_preflight_trust.py index 11513727d0ac..ddbcfd24d85d 100644 --- a/tests/test_preflight_trust.py +++ b/tests/test_preflight_trust.py @@ -6,6 +6,7 @@ import subprocess import tempfile import unittest +import git_fixture_env # noqa: F401 (disables git auto maintenance) SOURCE = Path(__file__).resolve().parents[1] diff --git a/tests/test_reuse_app_host_products.py b/tests/test_reuse_app_host_products.py index a4f7661ed132..30d71982dc4d 100644 --- a/tests/test_reuse_app_host_products.py +++ b/tests/test_reuse_app_host_products.py @@ -19,6 +19,7 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts/ci")) import reuse_app_host_products as reuse from test_app_host_test_products import TestProductHandoff +import git_fixture_env # noqa: F401 (disables git auto maintenance) class ReuseProducts(TestProductHandoff): diff --git a/tests/test_reuse_release_product.py b/tests/test_reuse_release_product.py index e32acf02a93d..2b0fd73d0437 100644 --- a/tests/test_reuse_release_product.py +++ b/tests/test_reuse_release_product.py @@ -16,6 +16,7 @@ ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / "scripts/ci")) import reuse_release_product as reuse +import git_fixture_env # noqa: F401 (disables git auto maintenance) class FakeGitHub: diff --git a/tests/test_seed_derived_data.py b/tests/test_seed_derived_data.py index 5e5d476d7bc3..acf3f259b24c 100644 --- a/tests/test_seed_derived_data.py +++ b/tests/test_seed_derived_data.py @@ -13,6 +13,8 @@ import yaml +import git_fixture_env # noqa: F401 (disables git auto maintenance) + ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / "scripts/ci")) import seed_derived_data as seed # noqa: E402 diff --git a/tests/test_tui_publish_workflow_security.py b/tests/test_tui_publish_workflow_security.py index 96503e1e7c64..4aef789885a5 100644 --- a/tests/test_tui_publish_workflow_security.py +++ b/tests/test_tui_publish_workflow_security.py @@ -11,6 +11,7 @@ import tomllib import yaml +import git_fixture_env ROOT = Path(__file__).resolve().parents[1] @@ -938,6 +939,7 @@ def prepare(attempt: int) -> tuple[subprocess.CompletedProcess[str], str, Path]: "GIT_CONFIG_VALUE_0": "https://github.com/manaflow-ai/cmux.git", } ) + git_fixture_env.without_auto_maintenance(environment) result = subprocess.run( ("bash",), input=prepare_script, diff --git a/tests/test_verification_receipt.py b/tests/test_verification_receipt.py index b7bba88785d7..246647bf80b1 100644 --- a/tests/test_verification_receipt.py +++ b/tests/test_verification_receipt.py @@ -7,6 +7,7 @@ from unittest.mock import patch import importlib.util +import git_fixture_env # noqa: F401 (disables git auto maintenance) REPO = Path(__file__).resolve().parents[1] spec = importlib.util.spec_from_file_location("verification_receipt", REPO / "scripts/verification_receipt.py") diff --git a/tests/test_verify_local.py b/tests/test_verify_local.py index fb15f29d7fa1..d1a794a8ca2f 100644 --- a/tests/test_verify_local.py +++ b/tests/test_verify_local.py @@ -13,6 +13,8 @@ import unittest from unittest.mock import patch +import git_fixture_env # noqa: F401 (disables git auto maintenance) + ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / "scripts")) spec = importlib.util.spec_from_file_location("verify_local", ROOT / "scripts/verify-local.py") diff --git a/tests/test_web_complexity_source_enumeration.py b/tests/test_web_complexity_source_enumeration.py index ffc1ede2c0b3..f3fcf6978d93 100644 --- a/tests/test_web_complexity_source_enumeration.py +++ b/tests/test_web_complexity_source_enumeration.py @@ -28,6 +28,7 @@ import tempfile import unittest from pathlib import Path +import git_fixture_env # noqa: F401 (disables git auto maintenance) ROOT = Path(__file__).resolve().parents[1] SCRIPT = ROOT / "web" / "scripts" / "check-complexity.mjs" diff --git a/tests/test_web_complexity_trusted_workflow.py b/tests/test_web_complexity_trusted_workflow.py index ca1fe48e1a6e..e44ddf521aaf 100644 --- a/tests/test_web_complexity_trusted_workflow.py +++ b/tests/test_web_complexity_trusted_workflow.py @@ -20,6 +20,7 @@ from pathlib import Path import yaml +import git_fixture_env # noqa: F401 (disables git auto maintenance) ROOT = Path(__file__).resolve().parents[1] WORKFLOW = ROOT / ".github" / "workflows" / "web-complexity-trusted.yml" diff --git a/tests/test_web_validation.py b/tests/test_web_validation.py index fbd24188c441..e5235d31a5c3 100644 --- a/tests/test_web_validation.py +++ b/tests/test_web_validation.py @@ -12,6 +12,7 @@ ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / "scripts/ci")) import web_validation as gate +import git_fixture_env # noqa: F401 (disables git auto maintenance) class WebValidationTests(unittest.TestCase): diff --git a/tests_v2/test_cli_new_workspace_background_metadata.py b/tests_v2/test_cli_new_workspace_background_metadata.py index b6fe481d77f3..2a1b0939fb38 100644 --- a/tests_v2/test_cli_new_workspace_background_metadata.py +++ b/tests_v2/test_cli_new_workspace_background_metadata.py @@ -122,7 +122,7 @@ def _create_git_repo(root: Path) -> tuple[Path, str]: stderr=subprocess.DEVNULL, ) subprocess.run( - ["git", "-c", "commit.gpgsign=false", "commit", "-m", "init"], + ["git", "-c", "commit.gpgsign=false", "-c", "maintenance.auto=false", "commit", "-m", "init"], cwd=repo, check=True, stdout=subprocess.DEVNULL, diff --git a/tests_v2/test_cli_new_workspace_external_git_branch_refresh.py b/tests_v2/test_cli_new_workspace_external_git_branch_refresh.py index 4e3911eafd44..1abf612fb42b 100644 --- a/tests_v2/test_cli_new_workspace_external_git_branch_refresh.py +++ b/tests_v2/test_cli_new_workspace_external_git_branch_refresh.py @@ -137,7 +137,7 @@ def _create_git_repo(root: Path) -> Path: stderr=subprocess.DEVNULL, ) subprocess.run( - ["git", "-c", "commit.gpgsign=false", "commit", "-m", "init"], + ["git", "-c", "commit.gpgsign=false", "-c", "maintenance.auto=false", "commit", "-m", "init"], cwd=repo, check=True, stdout=subprocess.DEVNULL,