Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration


extensions = [
"sphinx_design",
"sphinx_needs",
Expand Down
2 changes: 1 addition & 1 deletion src/extensions/score_metamodel/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ py_library(
],
visibility = ["//visibility:public"],
# TODO: Figure out if all requirements are needed or if we can break it down a bit
deps = all_requirements,
deps = all_requirements + ["@score_docs_as_code//src/helper_lib"],
)

score_py_pytest(
Expand Down
7 changes: 7 additions & 0 deletions src/extensions/score_metamodel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@
from sphinx_needs.config import NeedType
from sphinx_needs.data import NeedsInfoType, NeedsView, SphinxNeedsData

from src.helper_lib import (
find_git_root,
find_ws_root,
get_current_git_hash,
get_github_repo_info,
)

from .external_needs import connect_external_needs
from .log import CheckLogger

Expand Down
1 change: 0 additions & 1 deletion src/extensions/score_metamodel/external_needs.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ def _parse_bazel_external_need(s: str) -> ExternalNeedsSource | None:
return ExternalNeedsSource(
bazel_module=repo, path_to_target=path_to_target, target=target
)

# Unknown data target. Probably not a needs.json file.
return None

Expand Down
1 change: 1 addition & 0 deletions src/extensions/score_source_code_linker/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ py_library(
),
imports = ["."],
visibility = ["//visibility:public"],
deps = ["@score_docs_as_code//src/helper_lib"],
)

score_py_pytest(
Expand Down
102 changes: 14 additions & 88 deletions src/extensions/score_source_code_linker/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
source code links from a JSON file and add them to the needs.
"""

import subprocess
from collections import defaultdict
from copy import deepcopy
from pathlib import Path
Expand All @@ -28,15 +27,19 @@
from sphinx_needs.logging import get_logger

from src.extensions.score_source_code_linker.generate_source_code_links_json import (
find_git_root,
find_ws_root,
generate_source_code_links_json,
)
from src.extensions.score_source_code_linker.needlinks import (
DefaultNeedLink,
NeedLink,
load_source_code_links_json,
)
from src.helper_lib import (
find_git_root,
find_ws_root,
get_current_git_hash,
get_github_base_url,
)

LOGGER = get_logger(__name__)
# Outcomment this to enable more verbose logging
Expand All @@ -62,13 +65,13 @@ def setup_once(app: Sphinx, config: Config):
LOGGER.debug(f"DEBUG: Git root is {find_git_root()}")

# Run only for local files!
# ws_root is not set when running on external repositories (dependencies).
# ws_root is not set when running on any on bazel run command repositories (dependencies)
ws_root = find_ws_root()
if not ws_root:
return

# When BUILD_WORKSPACE_DIRECTORY is set, we are inside a git repository.
assert find_git_root(ws_root)
assert find_git_root()

# Extension: score_source_code_linker
app.add_config_value(
Expand Down Expand Up @@ -143,92 +146,15 @@ def group_by_need(source_code_links: list[NeedLink]) -> dict[str, list[NeedLink]
return source_code_links_by_need


def parse_git_output(str_line: str) -> str:
if len(str_line.split()) < 2:
LOGGER.warning(
"Got wrong input line from 'get_github_repo_info'. "
f"Input: {str_line}."
"Expected example: 'origin git@github.com:user/repo.git'"
)
return ""
url = str_line.split()[1] # Get the URL part
# Handle SSH format (git@github.com:user/repo.git)
if url.startswith("git@"):
path = url.split(":")[1]
else:
path = "/".join(url.split("/")[3:]) # Get part after github.com/
return path.replace(".git", "")


def get_github_repo_info(git_root_cwd: Path) -> str:
process = subprocess.run(
["git", "remote", "-v"], capture_output=True, text=True, cwd=git_root_cwd
)
repo = ""
for line in process.stdout.split("\n"):
if "origin" in line and "(fetch)" in line:
repo = parse_git_output(line)
break
else:
# If we do not find 'origin' we just take the first line
LOGGER.info(
"Did not find origin remote name. "
"Will now take first result from: 'git remote -v'"
)
repo = parse_git_output(process.stdout.split("\n")[0])
assert repo != "", (
"Remote repository is not defined. Make sure you have a remote set. "
"Check this via 'git remote -v'"
)
return repo


def get_git_root(git_root: Path = Path()) -> Path:
# This is kinda ugly, doing this to reduce type errors.
# There might be a nicer way to do this
if git_root == Path():
passed_git_root = find_git_root()
if passed_git_root is None:
return Path()
else:
passed_git_root = git_root
return passed_git_root


def get_github_base_url(git_root: Path = Path()) -> str:
passed_git_root = get_git_root(git_root)
repo_info = get_github_repo_info(passed_git_root)
return f"https://github.com/{repo_info}"


def get_github_link(
git_root: Path = Path(), needlink: NeedLink = DefaultNeedLink()
) -> str:
passed_git_root = get_git_root(git_root)
base_url = get_github_base_url(
passed_git_root
) # Pass git_root to avoid double lookup
def get_github_link(needlink: NeedLink = DefaultNeedLink()) -> str:
passed_git_root = find_git_root()
if passed_git_root is None:
passed_git_root = Path()
base_url = get_github_base_url()
current_hash = get_current_git_hash(passed_git_root)
return f"{base_url}/blob/{current_hash}/{needlink.file}#L{needlink.line}"


def get_current_git_hash(ws_root: Path) -> str:
try:
result = subprocess.run(
["git", "log", "-n", "1", "--pretty=format:%H"],
cwd=ws_root,
capture_output=True,
check=True,
)
decoded_result = result.stdout.strip().decode()

assert all(c in "0123456789abcdef" for c in decoded_result)
return decoded_result
except Exception as e:
LOGGER.warning(f"Unexpected error: {ws_root}", exc_info=e)
raise


# req-Id: tool_req__docs_dd_link_source_code_link
def inject_links_into_needs(app: Sphinx, env: BuildEnvironment) -> None:
"""
Expand Down Expand Up @@ -280,7 +206,7 @@ def inject_links_into_needs(app: Sphinx, env: BuildEnvironment) -> None:
need_as_dict = cast(dict[str, object], need)

need_as_dict["source_code_link"] = ", ".join(
f"{get_github_link(ws_root, n)}<>{n.file}:{n.line}" for n in needlinks
f"{get_github_link(n)}<>{n.file}:{n.line}" for n in needlinks
)

# NOTE: Removing & adding the need is important to make sure
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,31 +25,6 @@
store_source_code_links_json,
)


def find_ws_root() -> Path | None:
"""Find the current MODULE.bazel file"""
ws_dir = os.environ.get("BUILD_WORKSPACE_DIRECTORY", None)
return Path(ws_dir) if ws_dir else None


def find_git_root(start_path: str | Path = "") -> Path | None:
"""Find the git root directory starting from the given path or __file__."""
if start_path == "":
start_path = __file__

git_root = Path(start_path).resolve()
esbonio_search = False
while not (git_root / ".git").exists():
git_root = git_root.parent
if git_root == Path("/"):
# fallback to cwd when building with python -m sphinx docs _build -T
if esbonio_search:
return None
git_root = Path.cwd().resolve()
esbonio_search = True
return git_root


TAGS = [
"# " + "req-traceability:",
"# " + "req-Id:",
Expand Down
Loading
Loading