diff --git a/doc/cool-uris.yaml b/doc/cool-uris.yaml index cfbde266..4f38cf2c 100644 --- a/doc/cool-uris.yaml +++ b/doc/cool-uris.yaml @@ -1,12 +1,17 @@ # WARNING: This file is autogenerated by: scripts/ensure-stable-doc-urls.py # Do not edit manually. +__optional__: +- doxygen_crawl.html +- meson_options.html annotated.html: [] classes.html: [] +debugging.html: [] deprecated.html: [] dir_63ce773eee1f9b680e6e312b48cc99ca.html: [] dir_891596f32582d3133e8915e72908625f.html: [] dir_d44c64559bbebec7f509842c48db8b23.html: [] dir_e68e8157741866f444e17edd764ebbae.html: [] +doxygen_crawl.html: [] error-index.html: [] files.html: [] functions.html: [] @@ -33,8 +38,9 @@ group__x11.html: [] index.html: [] keymap-text-format-v1.html: - md_doc_keymap_format_text_v1.html -md_doc_quick_guide.html: [] -modules.html: [] +md_doc_2quick-guide.html: +- md_doc_quick_guide.html +meson_options.html: [] pages.html: [] rule-file-format.html: - md_doc_rules_format.html @@ -54,6 +60,8 @@ structxkb__keymap.html: [] structxkb__rule__names.html: [] structxkb__state.html: [] todo.html: [] +topics.html: +- modules.html user-configuration.html: - md_doc_user_configuration.html xkb-intro.html: [] diff --git a/scripts/ensure-stable-doc-urls.py b/scripts/ensure-stable-doc-urls.py index f40356b9..45d72431 100755 --- a/scripts/ensure-stable-doc-urls.py +++ b/scripts/ensure-stable-doc-urls.py @@ -28,12 +28,15 @@ class ExitCode(IntFlag): NORMAL = 0 INVALID_UPDATES = 1 << 4 MISSING_UPDATES = 1 << 5 + NON_UNIQUE_DIRECTIONS = 1 << 6 THIS_SCRIPT_PATH = Path(__file__) RELATIVE_SCRIPT_PATH = THIS_SCRIPT_PATH.relative_to(THIS_SCRIPT_PATH.parent.parent) REDIRECTION_DELAY = 6 # in seconds. Note: at least 6s for accessibility +REDIRECTION_TITLE = "xkbcommon: Page Redirection" +OPTIONAL_ENTRY = "__optional__" # NOTE: The redirection works with the HTML tag: . # See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta#http-equiv @@ -50,7 +53,7 @@ class ExitCode(IntFlag): - xkbcommon: Page Redirection + ${title}
@@ -87,6 +90,14 @@ def parse_page_update(update: str) -> Update: return updateʹ +def is_page_redirection(path: Path): + with path.open("rt", encoding="utf-8") as fd: + for line in fd: + if REDIRECTION_TITLE in line: + return True + return False + + def update_registry(registry_path: Path, doc_dir: Path, updates: Sequence[str]): """ Update the URL registry by: @@ -95,21 +106,35 @@ def update_registry(registry_path: Path, doc_dir: Path, updates: Sequence[str]): """ # Parse updates updates_ = dict(map(parse_page_update, updates)) + # Update + invalid_updates = set(updates_) # Load previous registry with registry_path.open("rt", encoding="utf-8") as fd: - registry = yaml.safe_load(fd) or {} + registry: dict[str, list[str]] = yaml.safe_load(fd) or {} + registryʹ = dict( + (canonical, aliases) + for canonical, aliases in registry.items() + if canonical != OPTIONAL_ENTRY + ) # Expected updates - missing_updates = set(file for file in registry if not (doc_dir / file).is_file()) - # Update - invalid_updates = set(updates_) - redirections = frozenset(chain(*registry.values())) + missing_updates = set( + canonical for canonical in registryʹ if not (doc_dir / canonical).is_file() + ) + # Ensure each page is unique + for d, rs in registryʹ.items(): + if clashes := frozenset(rs).intersection(registry): + print( + f"[ERROR] The following redirections of “{d}”", + f"clash with canonical directions: {clashes}", + ) + exit(ExitCode.NON_UNIQUE_DIRECTIONS) + redirections = frozenset(chain.from_iterable(registryʹ.values())) for file in glob.iglob("**/*.html", root_dir=doc_dir, recursive=True): # Skip redirection pages if file in redirections: continue # Get previous entry and potential update - old = updates_.get(file) - if old: + if old := updates_.get(file): # Update old entry invalid_updates.remove(file) entry = registry.get(old) @@ -137,8 +162,28 @@ def update_registry(registry_path: Path, doc_dir: Path, updates: Sequence[str]): exit_code |= ExitCode.INVALID_UPDATES if missing_updates: for old in missing_updates: - print(f"[ERROR] “{old}” not found and has no update.") - exit_code |= ExitCode.MISSING_UPDATES + # Handle older Doxygen versions + if old in registry.get(OPTIONAL_ENTRY, []): + print( + "[WARNING] Handling old Doxygen version:", + f"skip optional “{old}”", + ) + missing_updates.remove(old) + continue + old_redirections = registry[old] + for r in old_redirections: + path = doc_dir / r + if path.is_file() and not is_page_redirection(path): + print( + "[WARNING] Handling old Doxygen version:", + f"use “{r}” instead of “{old}” for the canonical direction", + ) + missing_updates.remove(old) + break + else: + print(f"[ERROR] “{old}” not found and has no update.") + if missing_updates: + exit_code |= ExitCode.MISSING_UPDATES if exit_code: print("[ERROR] Processing interrupted: please fix the errors above.") exit(exit_code.value) @@ -146,10 +191,7 @@ def update_registry(registry_path: Path, doc_dir: Path, updates: Sequence[str]): with registry_path.open("wt", encoding="utf-8") as fd: fd.write(f"# WARNING: This file is autogenerated by: {RELATIVE_SCRIPT_PATH}\n") fd.write("# Do not edit manually.\n") - yaml.dump( - registry, - fd, - ) + yaml.dump(registry, fd) def generate_redirections(registry_path: Path, doc_dir: Path): @@ -159,22 +201,47 @@ def generate_redirections(registry_path: Path, doc_dir: Path): cool = True # Load registry with registry_path.open("rt", encoding="utf-8") as fd: - registry = yaml.safe_load(fd) or {} - for canonical, aliases in registry.items(): + registry: dict[str, list[str]] = yaml.safe_load(fd) or {} + registryʹ = dict( + (canonical, aliases) + for canonical, aliases in registry.items() + if canonical != OPTIONAL_ENTRY + ) + for canonical, aliases in registryʹ.items(): # Check canonical path is up-to-date if not (doc_dir / canonical).is_file(): - cool = False - print( - f"ERROR: missing canonical documentation page “{canonical}”. " - f"Please update “{registry_path}” using {RELATIVE_SCRIPT_PATH}”." - ) + # Handle older Doxygen versions + if canonical in registry.get(OPTIONAL_ENTRY, []): + print( + "[WARNING] Handling old Doxygen version:", + f"skip optional “{canonical}”", + ) + continue + for r in aliases: + path = doc_dir / r + if path.is_file() and not is_page_redirection(path): + print( + "[WARNING] Handling old Doxygen version:", + f"use “{r}” instead of “{canonical}” for the canonical direction", + ) + canonical = r + aliases.remove(r) + break + else: + cool = False + print( + f"ERROR: missing canonical documentation page “{canonical}”. " + f"Please update “{registry_path}” using {RELATIVE_SCRIPT_PATH}”." + ) # Add a redirection page for alias in aliases: path = doc_dir / alias with path.open("wt", encoding="utf-8") as fd: fd.write( REDIRECTION_PAGE_TEMPLATE.substitute( - canonical=canonical, delay=REDIRECTION_DELAY + canonical=canonical, + delay=REDIRECTION_DELAY, + title=REDIRECTION_TITLE, ) ) if not cool: