2"""Stamp generated Doxygen HTML with the Git revision it documents."""
4from __future__
import annotations
9from pathlib
import Path
12REPO_ROOT = Path(__file__).resolve().parents[2]
17 @brief Return trimmed output from one Git command.
18 @param[in] args Arguments passed after `git`.
19 @return Decoded, trimmed Git stdout.
22 return subprocess.check_output([
"git", *args], cwd=REPO_ROOT, text=
True).strip()
27 @brief Resolve the PICurv release identity the same way the conductor does.
29 @details Mirrors `_source_build_identity()` in `picurv_cli/core.py` using only Git
30 and the `VERSION` file, so the docs build stays free of a `picurv_cli`
31 import (and its PETSc-adjacent dependency chain) while still reporting the
32 same `release_version` / `build_id` a user sees from `picurv version`.
33 @param[in] dirty Whether the working tree carries uncommitted tracked changes.
34 @return Mapping with `release_version`, `build_id`, and `released`.
37 release_version = (REPO_ROOT /
"VERSION").read_text(encoding=
"utf-8").strip()
38 short_sha =
git_output(
"rev-parse",
"--short=12",
"HEAD")
40 describe = subprocess.run(
41 [
"git",
"describe",
"--tags",
"--match", f
"v{release_version}",
"--long"],
42 cwd=REPO_ROOT, text=
True, capture_output=
True, check=
False,
44 if describe.returncode == 0:
45 parts = describe.stdout.strip().rsplit(
"-", 2)
46 if len(parts) == 3
and parts[1].isdigit():
47 dev_distance = int(parts[1])
49 dev_distance = int(
git_output(
"rev-list",
"--count",
"HEAD"))
50 released = dev_distance == 0
and not dirty
51 suffix =
"" if dev_distance
in (0,
None)
else f
".dev{dev_distance}"
52 build_id = f
"{release_version}{suffix}+g{short_sha}" + (
".dirty" if dirty
else "")
53 return {
"release_version": release_version,
"build_id": build_id,
"released": released}
58 @brief Parse generated-documentation stamping arguments.
59 @return Parsed command-line namespace.
62 parser = argparse.ArgumentParser(description=__doc__)
63 parser.add_argument(
"--html-dir", type=Path, required=
True, help=
"generated Doxygen HTML directory")
64 return parser.parse_args()
69 @brief Write revision metadata and load it on every generated HTML page.
70 @return Process status code.
74 html_dir = args.html_dir.resolve()
75 if not html_dir.is_dir():
76 raise SystemExit(f
"HTML directory does not exist: {html_dir}")
78 remote =
git_output(
"remote",
"get-url",
"origin")
79 if remote.startswith(
"git@github.com:"):
80 remote =
"https://github.com/" + remote[len(
"git@github.com:"):]
81 if remote.endswith(
".git"):
83 clean =
not bool(
git_output(
"status",
"--porcelain"))
86 "short_sha": sha[:12],
87 "commit_url": f
"{remote}/commit/{sha}",
91 (html_dir /
"picurv-docs-revision.js").write_text(
92 "window.PICURV_DOCS_REVISION = " + json.dumps(revision, sort_keys=
True) +
";\n",
95 for page
in html_dir.rglob(
"*.html"):
96 relative = page.relative_to(html_dir)
97 script_path =
"../picurv-docs-revision.js" if relative.parts[0] ==
"search" else "picurv-docs-revision.js"
98 content = page.read_text(encoding=
"utf-8")
99 if "picurv-docs-revision.js" not in content:
100 content = content.replace(
"</head>", f
'<script src="{script_path}"></script>\n</head>')
101 page.write_text(content, encoding=
"utf-8")
102 print(f
"Stamped generated documentation as PICurv {revision['build_id']} (commit {sha}).")
106if __name__ ==
"__main__":
107 raise SystemExit(
main())
str git_output(*str args)
Return trimmed output from one Git command.
argparse.Namespace parse_args()
Parse generated-documentation stamping arguments.
dict resolve_release_identity(bool dirty)
Resolve the PICurv release identity the same way the conductor does.
int main()
Write revision metadata and load it on every generated HTML page.