Coverage for /builds/svesch/cikit/src/cikit/macros/sphinx.py: 97%
60 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-10-09 14:17 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-10-09 14:17 +0000
1# SPDX-License-Identifier: GPL-3.0-or-later
2"""Sphinx macro helpers for cikit.
4This module prepares generated CI artifacts for Sphinx documentation
5builds. It copies generated files into the documentation tree and
6provides helpers for stable links inside generated reStructuredText
7files.
9Author:
10 Sven Schlender
11Date:
12 2026-03-10
13"""
15import logging
16import os
17import shutil
18from pathlib import Path
19from tempfile import TemporaryDirectory
21from cikit.artifacts import Artifact, ArtifactType
22from cikit.command import Command, run_command
23from cikit.env import write_file
24from cikit.errors import CiToolsError
25from cikit.rst_tools import get_sphinx_paths
28LOGGER = logging.getLogger(__name__)
31def generate_badges_include(env, docs_dir: Path, output_file: Path = Path("badges.inc")) -> Path:
32 """Generate an include file containing badge image directives.
34 Args:
35 env: Resolved CI run environment.
36 docs_dir: Sphinx source directory.
37 output_file: Target include file path relative to ``docs_dir``.
39 Returns:
40 Path to the generated include file.
42 Raises:
43 CiToolsError: If path resolution or file writing fails.
44 """
45 docs_dir = env.resolve_path(docs_dir)
46 output_file = docs_dir / output_file
47 output_dir = output_file.parent.relative_to(docs_dir)
48 include_base_dir = docs_dir / output_dir
49 static_dir = docs_dir / "_static"
50 paths = get_sphinx_paths()
52 lines = []
53 for artifact in env.get_artifacts(artifact_type=ArtifactType.BADGE):
54 target_path = static_dir / paths.artifact_path(artifact)
55 rel_path = os.path.relpath(target_path, start=include_base_dir)
57 lines.append(f".. image:: {Path(rel_path).as_posix()}")
58 lines.append(f" :alt: {Path(artifact.target_name).stem}")
59 lines.append(" :height: 20")
60 lines.append("")
62 write_file(output_file, "\n".join(lines))
63 return output_file
66def _copy_artifact(artifact: Artifact, target: Path):
67 """Copy one registered artifact to its Sphinx target.
69 Args:
70 artifact: Registered artifact to copy.
71 target: Destination path in the Sphinx source tree.
73 Raises:
74 OSError: If the artifact cannot be copied.
75 """
76 target.parent.mkdir(parents=True, exist_ok=True)
78 if artifact.path.is_dir():
79 shutil.copytree(artifact.path, target, dirs_exist_ok=True)
80 else:
81 shutil.copy2(artifact.path, target)
84def prepare(env, docs_dir: Path):
85 """Prepare the Sphinx source tree for documentation build.
87 Args:
88 env: Resolved CI run environment.
89 docs_dir: Sphinx source directory.
91 Raises:
92 CiToolsError: If cleaning or copying artifacts fails.
93 """
94 docs_dir = env.resolve_path(docs_dir)
95 static_dir = docs_dir / "_static"
96 badges_dir = static_dir / "badges"
97 artifacts_dir = static_dir / "artifacts"
98 generated_dir = docs_dir / "_generated"
99 paths = get_sphinx_paths()
101 try:
102 for path in (badges_dir, artifacts_dir, generated_dir):
103 if path.exists():
104 shutil.rmtree(path)
106 for artifact in env.get_artifacts():
107 if artifact.artifact_type == ArtifactType.RST:
108 target = generated_dir / artifact.target_name
109 else:
110 target = static_dir / paths.artifact_path(artifact)
112 _copy_artifact(artifact, target)
113 except OSError as exp:
114 raise CiToolsError(f"Error while preparing Sphinx input: {exp}") from exp
117def sphinx(env, docs_dir: Path) -> Artifact:
118 """Build and register HTML documentation.
120 Args:
121 env: Resolved CI run environment.
122 docs_dir: Sphinx source directory.
124 Returns:
125 Registered HTML documentation folder artifact.
126 """
127 docs_dir = env.resolve_path(docs_dir)
129 with TemporaryDirectory() as temp_dir:
130 output_dir = Path(temp_dir) / "html"
132 cmd = Command("sphinx-build", use_exec_tool=True)
133 cmd.add_subcommand("build", ["-b", "html", "-D", "cikit_data_dir=${CIKIT_DATA_DIR}",
134 str(docs_dir), str(output_dir)])
135 argv = env.argv(cmd, "build")
137 run_command(argv, live_output=True, cwd=env["PROJECT_ROOT"])
139 return env.copy_artifact(output_dir, ArtifactType.FOLDER)