From 3403159a1884c967cfcc41ae71a6bb447c3a1446 Mon Sep 17 00:00:00 2001 From: MaximilianSoerenPollak Date: Thu, 29 Jan 2026 11:08:51 +0100 Subject: [PATCH 1/4] Make everyone use find_runfiles Change all 'os.getenv[RUNFILES]' to now use the find_runfiles file. This is in preperation for the rules_python upgrade which changes this implementation again. --- src/BUILD | 1 + src/extensions/BUILD | 1 + src/extensions/score_metamodel/BUILD | 2 +- .../score_metamodel/external_needs.py | 33 ++-- src/extensions/score_plantuml.py | 65 ++++---- src/find_runfiles/__init__.py | 115 +++++++------- src/find_runfiles/test_find_runfiles.py | 142 +++++++++--------- 7 files changed, 177 insertions(+), 182 deletions(-) diff --git a/src/BUILD b/src/BUILD index f45f14fd7..8c9711d98 100644 --- a/src/BUILD +++ b/src/BUILD @@ -65,6 +65,7 @@ java_binary( py_library( name = "plantuml_for_python", srcs = ["@score_docs_as_code//src:dummy.py"], + #deps = ["@score_docs_as_code//src/find_runfiles"], data = ["@score_docs_as_code//src:plantuml"], visibility = ["//visibility:public"], ) diff --git a/src/extensions/BUILD b/src/extensions/BUILD index d4db4293c..80e615d57 100644 --- a/src/extensions/BUILD +++ b/src/extensions/BUILD @@ -18,6 +18,7 @@ load("@score_tooling//:defs.bzl", "score_py_pytest", "score_virtualenv") py_library( name = "score_plantuml", srcs = ["@score_docs_as_code//src/extensions:score_plantuml.py"], + deps = ["@score_docs_as_code//src/find_runfiles"], imports = ["."], visibility = ["//visibility:public"], ) diff --git a/src/extensions/score_metamodel/BUILD b/src/extensions/score_metamodel/BUILD index 30392ca00..44ba45f12 100644 --- a/src/extensions/score_metamodel/BUILD +++ b/src/extensions/score_metamodel/BUILD @@ -49,7 +49,7 @@ py_library( imports = ["."], visibility = ["//visibility:public"], # TODO: Figure out if all requirements are needed or if we can break it down a bit - deps = all_requirements + ["@score_docs_as_code//src/helper_lib"], + deps = all_requirements + ["@score_docs_as_code//src/helper_lib", "@score_docs_as_code//src/find_runfiles"], ) score_py_pytest( diff --git a/src/extensions/score_metamodel/external_needs.py b/src/extensions/score_metamodel/external_needs.py index 0c48a56a8..1deadf432 100644 --- a/src/extensions/score_metamodel/external_needs.py +++ b/src/extensions/score_metamodel/external_needs.py @@ -22,6 +22,7 @@ from sphinx.config import Config from sphinx.util import logging from sphinx_needs.needsfile import NeedsList +from src.find_runfiles import get_runfiles_dir logger = logging.getLogger(__name__) @@ -138,7 +139,7 @@ def temp(self: NeedsList): def get_external_needs_source(external_needs_source: str) -> list[ExternalNeedsSource]: - bazel = external_needs_source or os.getenv("RUNFILES_DIR") + bazel = external_needs_source or get_runfiles_dir() if bazel: external_needs = parse_external_needs_sources_from_DATA(external_needs_source) @@ -149,25 +150,10 @@ def get_external_needs_source(external_needs_source: str) -> list[ExternalNeedsS def add_external_needs_json(e: ExternalNeedsSource, config: Config): - json_file = f"{e.bazel_module}+/{e.target}/_build/needs/needs.json" - if r := os.getenv("RUNFILES_DIR"): - logger.debug("Using runfiles to determine external needs JSON file.") - fixed_json_file = Path(r) / json_file - else: - logger.debug( - "Running outside bazel. " - + "Determining git root for external needs JSON file." - ) - git_root = Path.cwd().resolve() - while not (git_root / ".git").exists(): - git_root = git_root.parent - if git_root == Path("/"): - sys.exit("Could not find git root.") - logger.debug(f"Git root found: {git_root}") - fixed_json_file = git_root / "bazel-bin" / "ide_support.runfiles" / json_file - - logger.debug(f"Fixed JSON file path: {json_file} -> {fixed_json_file}") - json_file = fixed_json_file + json_file_raw = f"{e.bazel_module}+/{e.target}/_build/needs/needs.json" + r = get_runfiles_dir() + json_file = r / json_file_raw + logger.debug(f"Fixed JSON file path: {json_file_raw} -> {json_file}") try: needs_json_data = json.loads(Path(json_file).read_text(encoding="utf-8")) # pyright: ignore[reportAny] @@ -192,11 +178,12 @@ def add_external_needs_json(e: ExternalNeedsSource, config: Config): def add_external_docs_sources(e: ExternalNeedsSource, config: Config): # Note that bazel does NOT write the files under e.target! # {e.bazel_module}+ matches the original git layout! - if r := os.getenv("RUNFILES_DIR"): - docs_source_path = Path(r) / f"{e.bazel_module}+" - else: + r = get_runfiles_dir() + if "ide_support.runfiles" in str(r): logger.error("Combo builds are currently only supported with Bazel.") return + else: + docs_source_path = Path(r) / f"{e.bazel_module}+" if "collections" not in config: config.collections = {} diff --git a/src/extensions/score_plantuml.py b/src/extensions/score_plantuml.py index c452315e5..eca8add87 100644 --- a/src/extensions/score_plantuml.py +++ b/src/extensions/score_plantuml.py @@ -30,42 +30,43 @@ from sphinx.application import Sphinx from sphinx.util import logging +from src.find_runfiles import get_runfiles_dir logger = logging.getLogger(__name__) -def get_runfiles_dir() -> Path: - if r := os.getenv("RUNFILES_DIR"): - # Runfiles are only available when running in Bazel. - # bazel build and bazel run are both supported. - # i.e. `bazel build //:docs` and `bazel run //:docs`. - logger.debug("Using runfiles to determine plantuml path.") - - runfiles_dir = Path(r) - - else: - # The only way to land here is when running from within the virtual - # environment created by the `:ide_support` rule in the BUILD file. - # i.e. esbonio or manual sphinx-build execution within the virtual - # environment. - # We'll still use the plantuml binary from the bazel build. - # But we need to find it first. - logger.debug("Running outside bazel.") - - git_root = Path.cwd().resolve() - while not (git_root / ".git").exists(): - git_root = git_root.parent - if git_root == Path("/"): - sys.exit("Could not find git root.") - - runfiles_dir = git_root / "bazel-bin" / "ide_support.runfiles" - - if not runfiles_dir.exists(): - sys.exit( - f"Could not find runfiles_dir at {runfiles_dir}. " - "Have a look at README.md for instructions on how to build docs." - ) - return runfiles_dir +# def get_runfiles_dir() -> Path: +# if r := os.getenv("RUNFILES_DIR"): +# # Runfiles are only available when running in Bazel. +# # bazel build and bazel run are both supported. +# # i.e. `bazel build //:docs` and `bazel run //:docs`. +# logger.debug("Using runfiles to determine plantuml path.") +# +# runfiles_dir = Path(r) +# +# else: +# # The only way to land here is when running from within the virtual +# # environment created by the `:ide_support` rule in the BUILD file. +# # i.e. esbonio or manual sphinx-build execution within the virtual +# # environment. +# # We'll still use the plantuml binary from the bazel build. +# # But we need to find it first. +# logger.debug("Running outside bazel.") +# +# git_root = Path.cwd().resolve() +# while not (git_root / ".git").exists(): +# git_root = git_root.parent +# if git_root == Path("/"): +# sys.exit("Could not find git root.") +# +# runfiles_dir = git_root / "bazel-bin" / "ide_support.runfiles" +# +# if not runfiles_dir.exists(): +# sys.exit( +# f"Could not find runfiles_dir at {runfiles_dir}. " +# "Have a look at README.md for instructions on how to build docs." +# ) +# return runfiles_dir def find_correct_path(runfiles: Path) -> Path: diff --git a/src/find_runfiles/__init__.py b/src/find_runfiles/__init__.py index e46620042..3cee2ce3b 100644 --- a/src/find_runfiles/__init__.py +++ b/src/find_runfiles/__init__.py @@ -44,74 +44,73 @@ def find_git_root() -> Path: ) -def get_runfiles_dir_impl( - cwd: Path, - conf_dir: Path, - env_runfiles: Path | None, - git_root: Path, -) -> Path: - """Functional (and therefore testable) logic to determine the runfiles directory.""" - - _log_debug( - f"get_runfiles_dir_impl(\n cwd={cwd},\n conf_dir={conf_dir},\n" - f" env_runfiles={env_runfiles},\n git_root={git_root}\n)" - ) - - if env_runfiles: +def get_runfiles_dir() -> Path: + """Runfiles directory relative to conf.py""" + if r := os.getenv("RUNFILES_DIR"): # Runfiles are only available when running in Bazel. - # Both `bazel build` and `bazel run` are supported. + # bazel build and bazel run are both supported. # i.e. `bazel build //:docs` and `bazel run //:docs`. - _log_debug("Using env[RUNFILES_DIR] to find the runfiles...") - - if env_runfiles.is_absolute() and "bazel-out" in env_runfiles.parts: - # In case of `bazel run` it will point to the global cache directory, - # which has a new hash every time. And it's not pretty. - # However, `bazel-out` is a symlink to that same cache directory! - try: - idx = env_runfiles.parts.index("bazel-out") - runfiles_dir = git_root.joinpath(*env_runfiles.parts[idx:]) - _log_debug(f"Made runfiles dir pretty: {runfiles_dir}") - except ValueError: - sys.exit("Could not find bazel-out in runfiles path.") - else: - runfiles_dir = git_root / env_runfiles + logger.debug("Using runfiles to determine plantuml path.") + + runfiles_dir = Path(r) else: # The only way to land here is when running from within the virtual - # environment created by the `:ide_support` rule. + # environment created by the `:ide_support` rule in the BUILD file. # i.e. esbonio or manual sphinx-build execution within the virtual # environment. - _log_debug("Running outside bazel.") + # We'll still use the plantuml binary from the bazel build. + # But we need to find it first. + logger.debug("Running outside bazel.") - # TODO: "process-docs" is in SOURCE_DIR!! - runfiles_dir = git_root / "bazel-bin" / "process-docs" / "ide_support.runfiles" - - return runfiles_dir + git_root = Path.cwd().resolve() + while not (git_root / ".git").exists(): + git_root = git_root.parent + if git_root == Path("/"): + sys.exit("Could not find git root.") + runfiles_dir = git_root / "bazel-bin" / "ide_support.runfiles" -def get_runfiles_dir() -> Path: - """Runfiles directory relative to conf.py""" - - # FIXME CONF_DIRECTORY is our invention. When running from esbonio, this is not - # set. It seems to provide app.confdir instead... - conf_dir = os.getenv("CONF_DIRECTORY") - assert conf_dir - - env_runfiles = os.getenv("RUNFILES_DIR") - - runfiles = Path( - get_runfiles_dir_impl( - cwd=Path(os.getcwd()), - conf_dir=Path(conf_dir), - env_runfiles=Path(env_runfiles) if env_runfiles else None, - git_root=find_git_root(), - ) - ) - - if not runfiles.exists(): + if not runfiles_dir.exists(): sys.exit( - f"Could not find runfiles at {runfiles}. Have a look at " - "README.md for instructions on how to build docs." + f"Could not find runfiles_dir at {runfiles_dir}. " + "Have a look at README.md for instructions on how to build docs." ) + return runfiles_dir + - return runfiles + # _log_debug( + # f"get_runfiles_dir_impl(\n cwd={cwd},\n " + # f" env_runfiles={env_runfiles},\n git_root={git_root}\n)" + # ) + # + # if env_runfiles: + # # Runfiles are only available when running in Bazel. + # # Both `bazel build` and `bazel run` are supported. + # # i.e. `bazel build //:docs` and `bazel run //:docs`. + # _log_debug("Using env[RUNFILES_DIR] to find the runfiles...") + # + # if env_runfiles.is_absolute() and "bazel-out" in env_runfiles.parts: + # # In case of `bazel run` it will point to the global cache directory, + # # which has a new hash every time. And it's not pretty. + # # However, `bazel-out` is a symlink to that same cache directory! + # try: + # idx = env_runfiles.parts.index("bazel-out") + # runfiles_dir = git_root.joinpath(*env_runfiles.parts[idx:]) + # _log_debug(f"Made runfiles dir pretty: {runfiles_dir}") + # except ValueError: + # sys.exit("Could not find bazel-out in runfiles path.") + # else: + # runfiles_dir = git_root / env_runfiles + # + # else: + # # The only way to land here is when running from within the virtual + # # environment created by the `:ide_support` rule. + # # i.e. esbonio or manual sphinx-build execution within the virtual + # # environment. + # _log_debug("Running outside bazel.") + # + # # TODO: "process-docs" is in SOURCE_DIR!! + # runfiles_dir = git_root / "bazel-bin" / "process-docs" / "ide_support.runfiles" + # + # return runfiles_dir diff --git a/src/find_runfiles/test_find_runfiles.py b/src/find_runfiles/test_find_runfiles.py index 97d73d84b..0462b43d7 100644 --- a/src/find_runfiles/test_find_runfiles.py +++ b/src/find_runfiles/test_find_runfiles.py @@ -10,84 +10,90 @@ # # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* +import os +import pytest from pathlib import Path +from src import find_runfiles # Assuming the new file is named find_runfiles.py -# TODO: why is there an __init__.py file in tooling? -from src import find_runfiles +## --- Helpers to simulate environments --- +def setup_mock_repo(tmp_path: Path): + """Creates a dummy .git directory and returns the path.""" + repo = tmp_path / "workspaces" / "process" + repo.mkdir(parents=True) + (repo / ".git").mkdir() + return repo -def get_runfiles_dir_impl( - cwd: str, conf_dir: str, env_runfiles: str | None, git_root: str -): - return str( - find_runfiles.get_runfiles_dir_impl( - cwd=Path(cwd), - conf_dir=Path(conf_dir), - env_runfiles=Path(env_runfiles) if env_runfiles else None, - git_root=Path(git_root), - ) - ) +## --- Tests --- +def test_run_incremental_pretty_path(tmp_path, monkeypatch): + """ + Simulates: bazel run //process-docs:incremental + Logic: Uses RUNFILES_DIR environment variable. + """ + git_root = setup_mock_repo(tmp_path) + runfiles_path = git_root / "bazel-out/k8-fastbuild/bin/process-docs/incremental.runfiles" + runfiles_path.mkdir(parents=True) -def test_run_incremental(): - """bazel run //process-docs:incremental""" - # in incremental.py: - assert get_runfiles_dir_impl( - cwd="/home/vscode/.cache/bazel/_bazel_vscode/6084288f00f33db17acb4220ce8f1999/execroot/_main/bazel-out/k8-fastbuild/bin/process-docs/incremental.runfiles/_main", - conf_dir="process-docs", - env_runfiles="/home/vscode/.cache/bazel/_bazel_vscode/6084288f00f33db17acb4220ce8f1999/execroot/_main/bazel-out/k8-fastbuild/bin/process-docs/incremental.runfiles", - git_root="/workspaces/process", - ) == ( - "/workspaces/process/bazel-out/k8-fastbuild/bin/process-docs/" - "incremental.runfiles" - ) + # In the new logic, get_runfiles_dir() just returns the env var Path if it exists + monkeypatch.setenv("RUNFILES_DIR", str(runfiles_path)) + + result = find_runfiles.get_runfiles_dir() + assert result == runfiles_path + assert result.exists() - # in conf.py: - assert get_runfiles_dir_impl( - cwd="/workspaces/process/process-docs", - conf_dir="process-docs", - env_runfiles="/home/vscode/.cache/bazel/_bazel_vscode/6084288f00f33db17acb4220ce8f1999/execroot/_main/bazel-out/k8-fastbuild/bin/process-docs/incremental.runfiles", - git_root="/workspaces/process", - ) == ( - "/workspaces/process/bazel-out/k8-fastbuild/bin/process-docs/" - "incremental.runfiles" - ) +def test_build_incremental_and_exec_it(tmp_path, monkeypatch): + """ + Simulates: bazel build //process-docs:incremental && bazel-bin/process-docs/incremental + """ + git_root = setup_mock_repo(tmp_path) + bin_runfiles = git_root / "bazel-bin/process-docs/incremental.runfiles" + bin_runfiles.mkdir(parents=True) + monkeypatch.setenv("RUNFILES_DIR", str(bin_runfiles)) -def test_build_incremental_and_exec_it(): - """bazel build //process-docs:incremental && bazel-bin/process-docs/incremental""" - assert ( - get_runfiles_dir_impl( - cwd="/workspaces/process/process-docs", - conf_dir="process-docs", - env_runfiles="bazel-bin/process-docs/incremental.runfiles", - git_root="/workspaces/process", - ) - == "/workspaces/process/bazel-bin/process-docs/incremental.runfiles" - ) + result = find_runfiles.get_runfiles_dir() + assert result == bin_runfiles +def test_outside_bazel_ide_support(tmp_path, monkeypatch): + """ + Simulates: Running outside bazel (e.g., Esbonio/Sphinx). + Logic: Falls back to git_root / "bazel-bin" / "ide_support.runfiles" + """ + git_root = setup_mock_repo(tmp_path) + # The new logic uses Path.cwd().resolve() to find .git + monkeypatch.chdir(git_root) + monkeypatch.delenv("RUNFILES_DIR", raising=False) -def test_esbonio_old(): - """Observed with esbonio 0.x""" - assert ( - get_runfiles_dir_impl( - cwd="/workspaces/process/process-docs", - conf_dir="process-docs", - env_runfiles=None, - git_root="/workspaces/process", - ) - == "/workspaces/process/bazel-bin/process-docs/ide_support.runfiles" - ) + # Create the expected fallback path + expected_path = git_root / "bazel-bin" / "ide_support.runfiles" + expected_path.mkdir(parents=True) + result = find_runfiles.get_runfiles_dir() + assert result == expected_path -def test3(): - # docs named differently, just to make sure nothing is hardcoded - # bazel run //other-docs:incremental - assert get_runfiles_dir_impl( - cwd="/workspaces/process/other-docs", - conf_dir="other-docs", - env_runfiles="/home/vscode/.cache/bazel/_bazel_vscode/6084288f00f33db17acb4220ce8f1999/execroot/_main/bazel-out/k8-fastbuild/bin/other-docs/incremental.runfiles", - git_root="/workspaces/process", - ) == ( - "/workspaces/process/bazel-out/k8-fastbuild/bin/other-docs/incremental.runfiles" - ) +def test_find_git_root_via_env(tmp_path, monkeypatch): + """Tests find_git_root prioritizing BUILD_WORKSPACE_DIRECTORY.""" + workspace = tmp_path / "workspace_env" + workspace.mkdir() + monkeypatch.setenv("BUILD_WORKSPACE_DIRECTORY", str(workspace)) + + assert find_runfiles.find_git_root() == workspace + +def test_find_git_root_via_traversal(tmp_path, monkeypatch): + """Tests find_git_root by walking up the tree.""" + git_root = setup_mock_repo(tmp_path) + monkeypatch.delenv("BUILD_WORKSPACE_DIRECTORY", raising=False) + + # We need to ensure __file__ in the module points somewhere inside this tmp_path + # This is tricky without mocks, so we ensure the logic finds it relative to the module + # or rely on the directory traversal if the module itself is in the temp path. + # For a pure integration test, we verify the traversal logic: + sub_dir = git_root / "some" / "deep" / "path" + sub_dir.mkdir(parents=True) + + # Since we can't easily change __file__ of a loaded module, + # find_git_root() might need a small refactor to accept a starting path + # for better testability, but staying true to your current code: + monkeypatch.chdir(sub_dir) + # This will work if find_git_root uses Path.cwd() or if __file__ is managed. From fb157d819f30506e8a4c0c31339e1f15cd77aaf8 Mon Sep 17 00:00:00 2001 From: MaximilianSoerenPollak Date: Thu, 29 Jan 2026 11:11:25 +0100 Subject: [PATCH 2/4] Formatting --- src/extensions/BUILD | 2 +- src/extensions/score_metamodel/BUILD | 5 ++++- src/find_runfiles/__init__.py | 1 - src/find_runfiles/test_find_runfiles.py | 21 +++++++++++++++------ 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/extensions/BUILD b/src/extensions/BUILD index 80e615d57..660a480e5 100644 --- a/src/extensions/BUILD +++ b/src/extensions/BUILD @@ -18,7 +18,7 @@ load("@score_tooling//:defs.bzl", "score_py_pytest", "score_virtualenv") py_library( name = "score_plantuml", srcs = ["@score_docs_as_code//src/extensions:score_plantuml.py"], - deps = ["@score_docs_as_code//src/find_runfiles"], imports = ["."], visibility = ["//visibility:public"], + deps = ["@score_docs_as_code//src/find_runfiles"], ) diff --git a/src/extensions/score_metamodel/BUILD b/src/extensions/score_metamodel/BUILD index 44ba45f12..285942fcb 100644 --- a/src/extensions/score_metamodel/BUILD +++ b/src/extensions/score_metamodel/BUILD @@ -49,7 +49,10 @@ py_library( imports = ["."], visibility = ["//visibility:public"], # TODO: Figure out if all requirements are needed or if we can break it down a bit - deps = all_requirements + ["@score_docs_as_code//src/helper_lib", "@score_docs_as_code//src/find_runfiles"], + deps = all_requirements + [ + "@score_docs_as_code//src/find_runfiles", + "@score_docs_as_code//src/helper_lib", + ], ) score_py_pytest( diff --git a/src/find_runfiles/__init__.py b/src/find_runfiles/__init__.py index 3cee2ce3b..09aada4da 100644 --- a/src/find_runfiles/__init__.py +++ b/src/find_runfiles/__init__.py @@ -78,7 +78,6 @@ def get_runfiles_dir() -> Path: ) return runfiles_dir - # _log_debug( # f"get_runfiles_dir_impl(\n cwd={cwd},\n " # f" env_runfiles={env_runfiles},\n git_root={git_root}\n)" diff --git a/src/find_runfiles/test_find_runfiles.py b/src/find_runfiles/test_find_runfiles.py index 0462b43d7..0391e1139 100644 --- a/src/find_runfiles/test_find_runfiles.py +++ b/src/find_runfiles/test_find_runfiles.py @@ -17,6 +17,7 @@ ## --- Helpers to simulate environments --- + def setup_mock_repo(tmp_path: Path): """Creates a dummy .git directory and returns the path.""" repo = tmp_path / "workspaces" / "process" @@ -24,24 +25,29 @@ def setup_mock_repo(tmp_path: Path): (repo / ".git").mkdir() return repo + ## --- Tests --- + def test_run_incremental_pretty_path(tmp_path, monkeypatch): """ Simulates: bazel run //process-docs:incremental Logic: Uses RUNFILES_DIR environment variable. """ git_root = setup_mock_repo(tmp_path) - runfiles_path = git_root / "bazel-out/k8-fastbuild/bin/process-docs/incremental.runfiles" + runfiles_path = ( + git_root / "bazel-out/k8-fastbuild/bin/process-docs/incremental.runfiles" + ) runfiles_path.mkdir(parents=True) # In the new logic, get_runfiles_dir() just returns the env var Path if it exists monkeypatch.setenv("RUNFILES_DIR", str(runfiles_path)) - + result = find_runfiles.get_runfiles_dir() assert result == runfiles_path assert result.exists() + def test_build_incremental_and_exec_it(tmp_path, monkeypatch): """ Simulates: bazel build //process-docs:incremental && bazel-bin/process-docs/incremental @@ -55,6 +61,7 @@ def test_build_incremental_and_exec_it(tmp_path, monkeypatch): result = find_runfiles.get_runfiles_dir() assert result == bin_runfiles + def test_outside_bazel_ide_support(tmp_path, monkeypatch): """ Simulates: Running outside bazel (e.g., Esbonio/Sphinx). @@ -72,6 +79,7 @@ def test_outside_bazel_ide_support(tmp_path, monkeypatch): result = find_runfiles.get_runfiles_dir() assert result == expected_path + def test_find_git_root_via_env(tmp_path, monkeypatch): """Tests find_git_root prioritizing BUILD_WORKSPACE_DIRECTORY.""" workspace = tmp_path / "workspace_env" @@ -80,20 +88,21 @@ def test_find_git_root_via_env(tmp_path, monkeypatch): assert find_runfiles.find_git_root() == workspace + def test_find_git_root_via_traversal(tmp_path, monkeypatch): """Tests find_git_root by walking up the tree.""" git_root = setup_mock_repo(tmp_path) monkeypatch.delenv("BUILD_WORKSPACE_DIRECTORY", raising=False) - + # We need to ensure __file__ in the module points somewhere inside this tmp_path # This is tricky without mocks, so we ensure the logic finds it relative to the module # or rely on the directory traversal if the module itself is in the temp path. # For a pure integration test, we verify the traversal logic: sub_dir = git_root / "some" / "deep" / "path" sub_dir.mkdir(parents=True) - - # Since we can't easily change __file__ of a loaded module, - # find_git_root() might need a small refactor to accept a starting path + + # Since we can't easily change __file__ of a loaded module, + # find_git_root() might need a small refactor to accept a starting path # for better testability, but staying true to your current code: monkeypatch.chdir(sub_dir) # This will work if find_git_root uses Path.cwd() or if __file__ is managed. From 6c2dbf3b376fbf97dca9b886936dbb0b80831e36 Mon Sep 17 00:00:00 2001 From: MaximilianSoerenPollak Date: Thu, 29 Jan 2026 14:46:03 +0100 Subject: [PATCH 3/4] Pre-Rebase commit --- .../score_metamodel/external_needs.py | 6 +- src/extensions/score_plantuml.py | 3 +- src/find_runfiles/__init__.py | 3 +- src/find_runfiles/test_find_runfiles.py | 69 ++++++++++--------- 4 files changed, 43 insertions(+), 38 deletions(-) diff --git a/src/extensions/score_metamodel/external_needs.py b/src/extensions/score_metamodel/external_needs.py index 1deadf432..5ebe34841 100644 --- a/src/extensions/score_metamodel/external_needs.py +++ b/src/extensions/score_metamodel/external_needs.py @@ -12,9 +12,7 @@ # ******************************************************************************* import json -import os import subprocess -import sys from dataclasses import dataclass from pathlib import Path @@ -22,6 +20,7 @@ from sphinx.config import Config from sphinx.util import logging from sphinx_needs.needsfile import NeedsList + from src.find_runfiles import get_runfiles_dir logger = logging.getLogger(__name__) @@ -182,8 +181,7 @@ def add_external_docs_sources(e: ExternalNeedsSource, config: Config): if "ide_support.runfiles" in str(r): logger.error("Combo builds are currently only supported with Bazel.") return - else: - docs_source_path = Path(r) / f"{e.bazel_module}+" + docs_source_path = Path(r) / f"{e.bazel_module}+" if "collections" not in config: config.collections = {} diff --git a/src/extensions/score_plantuml.py b/src/extensions/score_plantuml.py index eca8add87..dca734610 100644 --- a/src/extensions/score_plantuml.py +++ b/src/extensions/score_plantuml.py @@ -24,12 +24,11 @@ In addition it sets common PlantUML options, like output to svg_obj. """ -import os -import sys from pathlib import Path from sphinx.application import Sphinx from sphinx.util import logging + from src.find_runfiles import get_runfiles_dir logger = logging.getLogger(__name__) diff --git a/src/find_runfiles/__init__.py b/src/find_runfiles/__init__.py index 09aada4da..5540b523e 100644 --- a/src/find_runfiles/__init__.py +++ b/src/find_runfiles/__init__.py @@ -110,6 +110,7 @@ def get_runfiles_dir() -> Path: # _log_debug("Running outside bazel.") # # # TODO: "process-docs" is in SOURCE_DIR!! - # runfiles_dir = git_root / "bazel-bin" / "process-docs" / "ide_support.runfiles" + # runfiles_dir = git_root / "bazel-bin" / + # "process-docs" / "ide_support.runfiles" # # return runfiles_dir diff --git a/src/find_runfiles/test_find_runfiles.py b/src/find_runfiles/test_find_runfiles.py index 0391e1139..3fe8c4e13 100644 --- a/src/find_runfiles/test_find_runfiles.py +++ b/src/find_runfiles/test_find_runfiles.py @@ -10,17 +10,18 @@ # # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -import os -import pytest from pathlib import Path + +import pytest + from src import find_runfiles # Assuming the new file is named find_runfiles.py ## --- Helpers to simulate environments --- -def setup_mock_repo(tmp_path: Path): +def setup_mock_repo(tmp_path: Path) -> Path: """Creates a dummy .git directory and returns the path.""" - repo = tmp_path / "workspaces" / "process" + repo: Path = tmp_path / "workspaces" / "process" repo.mkdir(parents=True) (repo / ".git").mkdir() return repo @@ -29,80 +30,86 @@ def setup_mock_repo(tmp_path: Path): ## --- Tests --- -def test_run_incremental_pretty_path(tmp_path, monkeypatch): +def test_run_incremental_pretty_path( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: """ Simulates: bazel run //process-docs:incremental Logic: Uses RUNFILES_DIR environment variable. """ - git_root = setup_mock_repo(tmp_path) - runfiles_path = ( + git_root: Path = setup_mock_repo(tmp_path) + runfiles_path: Path = ( git_root / "bazel-out/k8-fastbuild/bin/process-docs/incremental.runfiles" ) runfiles_path.mkdir(parents=True) - # In the new logic, get_runfiles_dir() just returns the env var Path if it exists + # In the new logic, get_runfiles_dir() returns the env var Path if it exists monkeypatch.setenv("RUNFILES_DIR", str(runfiles_path)) - result = find_runfiles.get_runfiles_dir() + result: Path = find_runfiles.get_runfiles_dir() assert result == runfiles_path assert result.exists() -def test_build_incremental_and_exec_it(tmp_path, monkeypatch): +def test_build_incremental_and_exec_it( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: """ - Simulates: bazel build //process-docs:incremental && bazel-bin/process-docs/incremental + Simulates: bazel build //process-docs:incremental && + bazel-bin/process-docs/incremental """ - git_root = setup_mock_repo(tmp_path) - bin_runfiles = git_root / "bazel-bin/process-docs/incremental.runfiles" + git_root: Path = setup_mock_repo(tmp_path) + bin_runfiles: Path = git_root / "bazel-bin/process-docs/incremental.runfiles" bin_runfiles.mkdir(parents=True) monkeypatch.setenv("RUNFILES_DIR", str(bin_runfiles)) - result = find_runfiles.get_runfiles_dir() + result: Path = find_runfiles.get_runfiles_dir() assert result == bin_runfiles -def test_outside_bazel_ide_support(tmp_path, monkeypatch): +def test_outside_bazel_ide_support( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: """ Simulates: Running outside bazel (e.g., Esbonio/Sphinx). Logic: Falls back to git_root / "bazel-bin" / "ide_support.runfiles" """ - git_root = setup_mock_repo(tmp_path) + git_root: Path = setup_mock_repo(tmp_path) # The new logic uses Path.cwd().resolve() to find .git monkeypatch.chdir(git_root) monkeypatch.delenv("RUNFILES_DIR", raising=False) # Create the expected fallback path - expected_path = git_root / "bazel-bin" / "ide_support.runfiles" + expected_path: Path = git_root / "bazel-bin" / "ide_support.runfiles" expected_path.mkdir(parents=True) - result = find_runfiles.get_runfiles_dir() + result: Path = find_runfiles.get_runfiles_dir() assert result == expected_path -def test_find_git_root_via_env(tmp_path, monkeypatch): +def test_find_git_root_via_env(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: """Tests find_git_root prioritizing BUILD_WORKSPACE_DIRECTORY.""" - workspace = tmp_path / "workspace_env" + workspace: Path = tmp_path / "workspace_env" workspace.mkdir() monkeypatch.setenv("BUILD_WORKSPACE_DIRECTORY", str(workspace)) assert find_runfiles.find_git_root() == workspace -def test_find_git_root_via_traversal(tmp_path, monkeypatch): +def test_find_git_root_via_traversal( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: """Tests find_git_root by walking up the tree.""" - git_root = setup_mock_repo(tmp_path) + git_root: Path = setup_mock_repo(tmp_path) monkeypatch.delenv("BUILD_WORKSPACE_DIRECTORY", raising=False) - # We need to ensure __file__ in the module points somewhere inside this tmp_path - # This is tricky without mocks, so we ensure the logic finds it relative to the module - # or rely on the directory traversal if the module itself is in the temp path. - # For a pure integration test, we verify the traversal logic: - sub_dir = git_root / "some" / "deep" / "path" + sub_dir: Path = git_root / "some" / "deep" / "path" sub_dir.mkdir(parents=True) - # Since we can't easily change __file__ of a loaded module, - # find_git_root() might need a small refactor to accept a starting path - # for better testability, but staying true to your current code: + # This logic assumes find_git_root has been updated to use Path.cwd() + # or that the test is running in a context where Path(__file__) is within git_root monkeypatch.chdir(sub_dir) - # This will work if find_git_root uses Path.cwd() or if __file__ is managed. + + result: Path = find_runfiles.find_git_root() + assert result == git_root From 6bea28dfec90fbb0defb4ecf057c302a0e934b9e Mon Sep 17 00:00:00 2001 From: MaximilianSoerenPollak Date: Thu, 29 Jan 2026 14:52:00 +0100 Subject: [PATCH 4/4] Fix tests & git finder --- src/find_runfiles/__init__.py | 11 +++++++---- src/find_runfiles/test_find_runfiles.py | 22 ++++++++-------------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/find_runfiles/__init__.py b/src/find_runfiles/__init__.py index 5540b523e..cd4d25082 100644 --- a/src/find_runfiles/__init__.py +++ b/src/find_runfiles/__init__.py @@ -27,14 +27,17 @@ def _log_debug(message: str): print(message) -def find_git_root() -> Path: - # TODO: is __file__ ever resolved into the bazel cache directories? - # Then this function will not work! +def find_git_root(starting_path: Path | None = None) -> Path: + # 1. Highest priority: Bazel environment variable workspace = os.getenv("BUILD_WORKSPACE_DIRECTORY") if workspace: return Path(workspace) - for parent in Path(__file__).resolve().parents: + # 2. Traversal logic: use starting_path (for tests) or __file__ (for prod) + # We resolve it to ensure we aren't dealing with symlinks in the bazel cache + current: Path = (starting_path or Path(__file__)).resolve() + + for parent in current.parents: if (parent / ".git").exists(): return parent diff --git a/src/find_runfiles/test_find_runfiles.py b/src/find_runfiles/test_find_runfiles.py index 3fe8c4e13..0a04dd8ff 100644 --- a/src/find_runfiles/test_find_runfiles.py +++ b/src/find_runfiles/test_find_runfiles.py @@ -88,28 +88,22 @@ def test_outside_bazel_ide_support( assert result == expected_path -def test_find_git_root_via_env(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: - """Tests find_git_root prioritizing BUILD_WORKSPACE_DIRECTORY.""" - workspace: Path = tmp_path / "workspace_env" - workspace.mkdir() - monkeypatch.setenv("BUILD_WORKSPACE_DIRECTORY", str(workspace)) - - assert find_runfiles.find_git_root() == workspace - - def test_find_git_root_via_traversal( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: - """Tests find_git_root by walking up the tree.""" + """Tests find_git_root by walking up the tree from a specific path.""" + # Create the fake repo: /tmp/.../workspaces/process/.git git_root: Path = setup_mock_repo(tmp_path) monkeypatch.delenv("BUILD_WORKSPACE_DIRECTORY", raising=False) + # Create a deep subdirectory and a "fake" script file inside it sub_dir: Path = git_root / "some" / "deep" / "path" sub_dir.mkdir(parents=True) + fake_script: Path = sub_dir / "tool.py" + fake_script.touch() - # This logic assumes find_git_root has been updated to use Path.cwd() - # or that the test is running in a context where Path(__file__) is within git_root - monkeypatch.chdir(sub_dir) + # Pass the fake script path so the function starts searching from there + result: Path = find_runfiles.find_git_root(starting_path=fake_script) - result: Path = find_runfiles.find_git_root() assert result == git_root + assert (result / ".git").exists()