1
0
mirror of https://github.com/systemd/systemd synced 2024-07-09 04:26:06 +00:00
systemd/tools/check-version-history.py
Abderrahim Kitouni 3691e7fce7 man: add checks for missing version information
This adds a new script tools/check-version-history.py and a corresponding
test when building in developer mode. It checks manpages (except dbus
documentation which is handled by update-dbus-docs) for missing version
history information.

It also adds ignore lists based on version 183 (the version that our version
annotations go back to). These can be augmented if we want to ignore other
elements if it doesn't make sense for them to have version annotations.
2023-10-01 11:54:29 +01:00

136 lines
4.2 KiB
Python

#!/usr/bin/env python3
# SPDX-License-Identifier: LGPL-2.1-or-later
import os
import sys
import lxml.etree as tree
_parser = tree.XMLParser(resolve_entities=False)
tree.set_default_parser(_parser)
def find_undocumented_functions(pages, ignorelist):
undocumented = []
for page in pages:
filename = os.path.basename(page)
pagetree = tree.parse(page)
assert pagetree.getroot().tag == "refentry"
hist_section = pagetree.find("refsect1[title='History']")
for func in pagetree.findall("//funcprototype/funcdef/function"):
path = f"/refsynopsisdiv/funcsynopsis/funcprototype/funcdef/function[.='{func.text}']"
assert pagetree.findall(path) == [func]
if (
hist_section is None
or hist_section.find(f"para/function[.='{func.text}()']") is None
):
if func.text not in ignorelist:
undocumented.append((filename, func.text))
return undocumented
def construct_path(element):
tag = element.tag
if tag == "refentry":
return ""
predicate = ""
if tag == "varlistentry":
text = "".join(element.find("term").itertext())
predicate = f'[term="{text}"]'
elif tag.startswith("refsect"):
text = "".join(element.find("title").itertext())
predicate = f'[title="{text}"]'
elif tag == "variablelist":
varlists = element.getparent().findall(tag)
if len(varlists) > 1:
predicate = f"[{varlists.index(element)+1}]"
return construct_path(element.getparent()) + "/" + tag + predicate
def find_undocumented_commands(pages, ignorelist):
undocumented = []
for page in pages:
filename = os.path.basename(page)
pagetree = tree.parse(page)
if pagetree.getroot().tag != "refentry":
continue
for varlistentry in pagetree.findall("*//variablelist/varlistentry"):
path = construct_path(varlistentry)
assert pagetree.findall(path) == [varlistentry]
listitem = varlistentry.find("listitem")
parent = listitem if listitem is not None else varlistentry
rev = parent.getchildren()[-1]
if rev.get("href") != "version-info.xml":
if (filename, path) not in ignorelist:
undocumented.append((filename, path))
return undocumented
def process_pages(pages):
command_pages = []
function_pages = []
for page in pages:
filename = os.path.basename(page)
if filename.startswith("org.freedesktop."): # dbus
continue
if (
filename.startswith("sd_")
or filename.startswith("sd-")
or filename.startswith("udev_")
):
function_pages.append(page)
continue
command_pages.append(page)
undocumented_commands = find_undocumented_commands(
command_pages, command_ignorelist
)
undocumented_functions = find_undocumented_functions(
function_pages, function_ignorelist
)
return undocumented_commands, undocumented_functions
if __name__ == "__main__":
with open(os.path.join(os.path.dirname(__file__), "command_ignorelist")) as f:
command_ignorelist = []
for l in f.read().splitlines():
if l.startswith("#"):
continue
fname, path = l.split(" ", 1)
path = path.replace("\\n", "\n")
command_ignorelist.append((fname, path))
with open(os.path.join(os.path.dirname(__file__), "function_ignorelist")) as f:
function_ignorelist = f.read().splitlines()
undocumented_commands, undocumented_functions = process_pages(sys.argv[1:])
if undocumented_commands or undocumented_functions:
for filename, func in undocumented_functions:
print(
f"Function {func}() in {filename} isn't documented in the History section."
)
for filename, path in undocumented_commands:
print(filename, path, "is undocumented")
if undocumented_commands:
print(
"Hint: if you reorganized this part of the documentation, "
"please update tools/commands_ignorelist."
)
sys.exit(1)