diff --git a/man/build_manual_gallery.py b/man/build_manual_gallery.py
index cc78157bb33..c31ee46f0a4 100755
--- a/man/build_manual_gallery.py
+++ b/man/build_manual_gallery.py
@@ -200,7 +200,7 @@ def main(ext):
if ext == "html":
output.write(header_graphical_index_tmpl)
output.write('
\n')
- for image, filename in sorted(img_files.items(), key=operator.itemgetter(1)):
+ for image, filename in sorted(img_files.items(), key=operator.itemgetter(1, 0)):
name = get_module_name(filename, ext)
title = title_from_names(name, image)
if ext == "html":
diff --git a/utils/generate_last_commit_file.py b/utils/generate_last_commit_file.py
index e9d91bad3a4..04864f5431d 100644
--- a/utils/generate_last_commit_file.py
+++ b/utils/generate_last_commit_file.py
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
"""
-Script for creating an core_modules_with_last_commit.json file contains
-all core modules with their last commit. Used by GitHub "Create new
-release draft" action workflow.
+Script for creating a core_modules_with_last_commit.json file which
+contains every documentation page with its last commit. Used by GitHub
+"Create new release draft" action workflow.
JSON file structure:
@@ -33,7 +33,8 @@
def get_last_commit(src_dir):
- """Generate core modules JSON object with the following structure
+ """Generate JSON object with an entry per documentation page with the
+ following structure
"r.pack": {
"commit": "547ff44e6aecfb4c9cbf6a4717fc14e521bec0be",
@@ -43,17 +44,26 @@ def get_last_commit(src_dir):
commit key value is commit hash
date key value is author date
+ Entries are keyed by the name of each .html or .md documentation page,
+ so that pages whose name differs from their directory (r3.mapcalc,
+ r.watershed, the wxGUI.* pages, the intro pages, ...) and pages which
+ only exist as Markdown (the doc/ development pages) get an entry too.
+ The recorded commit is the last commit touching the directory the page
+ lives in, matching what a build with Git history shows for that page.
+
:param str src_dir: root source code dir
- :return JSON obj result: core modules with last commit and commit
- date
+ :return JSON obj result: documentation pages with last commit and
+ commit date
"""
result = {}
- join_sep = ","
if not shutil.which("git"):
sys.exit("Git command was not found. Please install it.")
- for root, _, files in os.walk(src_dir):
- if ".html{}".format(join_sep) not in join_sep.join(files) + join_sep:
+ for root, dirs, files in os.walk(src_dir):
+ # sort + skip hidden dirs
+ dirs[:] = sorted(d for d in dirs if not d.startswith("."))
+ pages = [f for f in files if f.endswith((".html", ".md"))]
+ if not pages:
continue
rel_path = os.path.relpath(root)
process_result = subprocess.run(
@@ -79,10 +89,11 @@ def get_last_commit(src_dir):
f"Cannot parse output from git log for '{rel_path}': "
f"{text} because {error}"
)
- result[os.path.basename(rel_path)] = {
- "commit": commit,
- "date": date,
- }
+ for page in sorted(pages):
+ result[os.path.splitext(page)[0]] = {
+ "commit": commit,
+ "date": date,
+ }
else:
sys.exit(process_result.stderr.decode())
return result
@@ -99,7 +110,7 @@ def main():
),
"w",
) as f:
- json.dump(get_last_commit(src_dir), f, indent=4)
+ json.dump(get_last_commit(src_dir), f, indent=4, sort_keys=True)
if __name__ == "__main__":
diff --git a/utils/mkhtml.py b/utils/mkhtml.py
index e8e55bbedeb..8da9f1d86cc 100644
--- a/utils/mkhtml.py
+++ b/utils/mkhtml.py
@@ -335,10 +335,16 @@ def update_toc(data):
# process header
src_data = read_file(src_file)
+# The meta page comments override the page title used in the generated
+# header and source code section. The title must stay separate from pgm,
+# which is the page name used to look up the last commit and the addon
+# path, where the overridden title (e.g. "LRS" for the lrs page) would
+# not match.
+pgm_title = pgm
name = re.search(r"()", src_data, re.IGNORECASE)
pgm_desc = "GRASS Reference Manual"
if name:
- pgm = name.group(2).strip().split("-", 1)[0].strip()
+ pgm_title = name.group(2).strip().split("-", 1)[0].strip()
name_desc = re.search(
r"()", src_data, re.IGNORECASE
)
@@ -346,7 +352,7 @@ def update_toc(data):
pgm_desc = name_desc.group(2).strip()
desc = re.search(r"()", src_data, re.IGNORECASE)
if desc:
- pgm = desc.group(2).strip()
+ pgm_title = desc.group(2).strip()
header_tmpl = string.Template(header_base + header_nopgm)
elif not pgm_desc:
header_tmpl = string.Template(header_base + header_pgm)
@@ -380,7 +386,7 @@ def update_toc(data):
",".join(new_keywords_paths),
)
if not re.search(r"", tmp_data, re.IGNORECASE):
- sys.stdout.write(header_tmpl.substitute(PGM=pgm, PGM_DESC=pgm_desc))
+ sys.stdout.write(header_tmpl.substitute(PGM=pgm_title, PGM_DESC=pgm_desc))
if tmp_data:
header_logo_img_el = '
'
@@ -510,7 +516,7 @@ def to_title(name):
sys.stdout.write(
sourcecode.substitute(
URL_SOURCE=url_source,
- PGM=pgm,
+ PGM=pgm_title,
URL_LOG=url_log,
DATE_TAG=date_tag,
)
diff --git a/utils/test_generate_last_commit_file.py b/utils/test_generate_last_commit_file.py
index 388f9632da3..21429e8f732 100644
--- a/utils/test_generate_last_commit_file.py
+++ b/utils/test_generate_last_commit_file.py
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
"""
-Script for testing an core_modules_with_last_commit.json file contains
-all core modules with their last commit. Used by GitHub "Additional Checks"
-action workflow.
+Script for testing that the core_modules_with_last_commit.json file
+contains every documentation page with its last commit. Used by GitHub
+"Additional Checks" action workflow.
Python lib dependencies:
@@ -47,33 +47,36 @@ def test_json_file_is_not_empty(read_json_file):
assert len(read_json_file) > 0
+# Pairs of documentation page name and the directory its source lives in.
+# Besides plain tools, they cover pages whose name differs from their
+# directory name and pages which only exist as Markdown, which both rely
+# on entries being keyed by page name.
+PAGES = [
+ ("v.surf.rst", os.path.join("vector", "v.surf.rst")),
+ ("r.info", os.path.join("raster", "r.info")),
+ ("r3.mapcalc", os.path.join("raster", "r.mapcalc")),
+ ("r.watershed", os.path.join("raster", "r.watershed", "front")),
+ ("wxGUI.components", os.path.join("gui", "wxpython", "docs")),
+ ("databaseintro", "db"),
+ ("style_guide", os.path.join("doc", "development")),
+ ("python_intro", "doc"),
+]
+
+
@pytest.mark.depends(on=["test_json_file_is_not_empty"])
-@pytest.mark.parametrize(
- "core_module_path",
- [
- os.path.join("vector", "v.surf.rst"),
- os.path.join("raster", "r.info"),
- ],
-)
-def test_core_modules_in_json_file(read_json_file, core_module_path):
- core_module = os.path.basename(core_module_path)
- assert core_module in read_json_file
+@pytest.mark.parametrize(("page", "page_path"), PAGES)
+def test_pages_in_json_file(read_json_file, page, page_path):
+ assert page in read_json_file
@pytest.mark.depends(
on=[
"test_json_file_is_not_empty",
- "test_core_modules_in_json_file",
+ "test_pages_in_json_file",
]
)
-@pytest.mark.parametrize(
- "core_module_path",
- [
- os.path.join("vector", "v.surf.rst"),
- os.path.join("raster", "r.info"),
- ],
-)
-def test_compare_json_file_data(read_json_file, core_module_path):
+@pytest.mark.parametrize(("page", "page_path"), PAGES)
+def test_compare_json_file_data(read_json_file, page, page_path):
# Get Git commit and commit date from local Git
process_result = subprocess.run(
[
@@ -81,13 +84,12 @@ def test_compare_json_file_data(read_json_file, core_module_path):
"log",
"-1",
f"--format=%H,{COMMIT_DATE_FORMAT}",
- core_module_path,
+ page_path,
],
capture_output=True,
check=True,
) # --format=%H,COMMIT_DATE_FORMAT commit hash,author date
commit, date = process_result.stdout.decode().strip().split(",")
- core_module = os.path.basename(core_module_path)
# Compare commit and commit date
- assert read_json_file[core_module]["commit"] == commit
- assert read_json_file[core_module]["date"] == date
+ assert read_json_file[page]["commit"] == commit
+ assert read_json_file[page]["date"] == date