diff --git a/src/helper_lib/__init__.py b/src/helper_lib/__init__.py index 74e275a1a..8c43ec823 100644 --- a/src/helper_lib/__init__.py +++ b/src/helper_lib/__init__.py @@ -14,6 +14,7 @@ import os import subprocess import sys +from enum import Enum from pathlib import Path from typing import Any @@ -24,6 +25,12 @@ LOGGER = get_logger(__name__) +class ExecutionEnvironment(Enum): + BAZEL_RUN = "bazel_run" + BAZEL_BUILD = "bazel_build" + DIRECT = "direct" + + def config_setdefault(config: Config, name: str, value: Any) -> None: """Set a Sphinx config value only if not explicitly set it in conf.py.""" @@ -47,6 +54,15 @@ def find_ws_root() -> Path | None: return Path(ws_dir) if ws_dir else None +def identify_environment() -> ExecutionEnvironment: + """Identify how the current Python process was started.""" + if find_ws_root() is not None: + return ExecutionEnvironment.BAZEL_RUN + if Runfiles.Create() is not None: + return ExecutionEnvironment.BAZEL_BUILD + return ExecutionEnvironment.DIRECT + + def find_git_root() -> Path | None: """ Find the git root directory, starting from workspace root or current directory. @@ -56,13 +72,22 @@ def find_git_root() -> Path | None: - 'bazel build' => ❌ None (sandbox has no .git) - 'direct sphinx' => ✅ Git root path (fallback to cwd) """ - start_path = find_ws_root() - if start_path is None: - start_path = Path.cwd() - git_root = Path(start_path).resolve() + match identify_environment(): + case ExecutionEnvironment.BAZEL_RUN: + # A Bazel workspace can be nested inside a larger Git repository, + # so the workspace is only the starting point for the search below. + start_path = find_ws_root() + assert start_path is not None + case ExecutionEnvironment.BAZEL_BUILD: + # Bazel build actions run inside a sandbox without a Git worktree. + return None + case ExecutionEnvironment.DIRECT: + start_path = Path.cwd() + + git_root = start_path.resolve() while not (git_root / ".git").exists(): git_root = git_root.parent - if git_root == Path("/"): + if git_root == git_root.parent: return None return git_root diff --git a/src/helper_lib/test_helper_lib.py b/src/helper_lib/test_helper_lib.py index dfca2b261..be3ade7b3 100644 --- a/src/helper_lib/test_helper_lib.py +++ b/src/helper_lib/test_helper_lib.py @@ -20,6 +20,7 @@ from src.helper_lib import ( config_setdefault, + find_git_root, get_current_git_hash, get_github_repo_info, get_runfiles_dir, @@ -82,6 +83,21 @@ def git_repo(temp_dir: Path) -> Path: return git_dir +def test_find_git_root_ignores_bazel_sandbox_ancestor_git_repo( + temp_dir: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + """A sandbox must not discover a Git repository outside its execroot.""" + (temp_dir / ".git").mkdir() + sandbox_dir = temp_dir / "sandbox" / "linux-sandbox" / "1" / "execroot" / "_main" + sandbox_dir.mkdir(parents=True) + monkeypatch.chdir(sandbox_dir) + monkeypatch.delenv("BUILD_WORKSPACE_DIRECTORY", raising=False) + monkeypatch.delenv("RUNFILES_MANIFEST_FILE", raising=False) + monkeypatch.setenv("RUNFILES_DIR", str(temp_dir / "runfiles")) + + assert find_git_root() is None + + @pytest.fixture def git_repo_multiple_remotes(temp_dir: Path) -> Path: """Create a git repository with multiple remotes for testing."""