|
@@ -0,0 +1,273 @@
|
|
|
+#!/usr/bin/env python3
|
|
|
+# SPDX-License-Identifier: MIT
|
|
|
+
|
|
|
+"""
|
|
|
+A Python script to update the common contents of a command example across all languages.
|
|
|
+
|
|
|
+Usage:
|
|
|
+ python3 scripts/update-command.py [-c] [-u] [-n] <PLATFORM> <FILENAME>
|
|
|
+
|
|
|
+Options:
|
|
|
+ -c, --common-part COMMON_PART
|
|
|
+ Specify the common part to be modified (any content between double brackets will be ignored).
|
|
|
+ -u, --updated-common-part UPDATED_COMMON_PART
|
|
|
+ Specify the updated common part (any content between double brackets will be ignored).
|
|
|
+ -n, --dry-run
|
|
|
+ Show what changes would be made without actually modifying the page.
|
|
|
+
|
|
|
+
|
|
|
+Examples:
|
|
|
+ 1. Update 'cargo' page interactively:
|
|
|
+ python3 scripts/update-command.py common cargo
|
|
|
+ Enter the command examples (any content between double curly brackets will be ignored):
|
|
|
+ Enter the common part to modify: cargo search {{}}
|
|
|
+ Enter the change to be made: cargo search --limit 1 {{}}
|
|
|
+
|
|
|
+ 2. Show what changes would be made by updating `sudo apt install {{}}` in 'apt' page to `sudo apt install {{}} --no-confirm`:
|
|
|
+ python3 scripts/update-command.py --dry-run -c "sudo apt install {{}}" -u "sudo apt install {{}} --no-confirm" linux apt
|
|
|
+"""
|
|
|
+
|
|
|
+from pathlib import Path
|
|
|
+import os
|
|
|
+import re
|
|
|
+import argparse
|
|
|
+import sys
|
|
|
+from functools import reduce
|
|
|
+import logging
|
|
|
+
|
|
|
+
|
|
|
+class MyFormatter(logging.Formatter):
|
|
|
+ grey = "\x1b[0;30m"
|
|
|
+ yellow = "\x1b[33;20m"
|
|
|
+ red = "\x1b[31;20m"
|
|
|
+ bold_red = "\x1b[31;1m"
|
|
|
+ reset = "\x1b[0m"
|
|
|
+ format = "%(levelname)s: %(message)s (%(filename)s:%(lineno)d)"
|
|
|
+
|
|
|
+ FORMATS = {
|
|
|
+ logging.INFO: grey + format + reset,
|
|
|
+ logging.WARNING: yellow + format + reset,
|
|
|
+ logging.ERROR: red + format + reset,
|
|
|
+ }
|
|
|
+
|
|
|
+ def format(self, record):
|
|
|
+ log_fmt = self.FORMATS.get(record.levelno)
|
|
|
+ formatter = logging.Formatter(log_fmt)
|
|
|
+ return formatter.format(record)
|
|
|
+
|
|
|
+
|
|
|
+logger = logging.getLogger(__name__)
|
|
|
+logger.propagate = False
|
|
|
+
|
|
|
+ch = logging.StreamHandler()
|
|
|
+ch.setFormatter(MyFormatter())
|
|
|
+
|
|
|
+logger.addHandler(ch)
|
|
|
+
|
|
|
+
|
|
|
+def get_locales(base_path: Path) -> list[str]:
|
|
|
+ return [
|
|
|
+ d.name.split(".")[1]
|
|
|
+ for d in base_path.iterdir()
|
|
|
+ if d.is_dir() and d.name.startswith("pages.")
|
|
|
+ ]
|
|
|
+
|
|
|
+
|
|
|
+def take_cmd_example_with_common_part(cmd_examples: list[str], common_part: str) -> str:
|
|
|
+ return next(
|
|
|
+ (
|
|
|
+ f"`{cmd_example}`"
|
|
|
+ for cmd_example in cmd_examples
|
|
|
+ if remove_placeholders(cmd_example) == common_part
|
|
|
+ ),
|
|
|
+ None,
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+def get_cmd_examples_of_page(page_text: str) -> list[str]:
|
|
|
+ command_pattern = re.compile(r"`([^`]+)`")
|
|
|
+ return re.findall(command_pattern, page_text)
|
|
|
+
|
|
|
+
|
|
|
+def find_cmd_example_with_common_part(common_part: str, page_text: str) -> list[str]:
|
|
|
+ cmd_examples = get_cmd_examples_of_page(page_text)
|
|
|
+ return take_cmd_example_with_common_part(cmd_examples, common_part)
|
|
|
+
|
|
|
+
|
|
|
+def get_page_path(tldr_root: Path, locale: str, platform: str, filename: str):
|
|
|
+ if locale == "":
|
|
|
+ return tldr_root / "pages" / platform / filename
|
|
|
+ return tldr_root / f"pages.{locale}" / platform / filename
|
|
|
+
|
|
|
+
|
|
|
+def split_by_curly_brackets(s: str) -> list[str]:
|
|
|
+ return re.split(r"(\{\{.*?\}\})", s)
|
|
|
+
|
|
|
+
|
|
|
+def parse_placeholders(cmd_example: str) -> list[str]:
|
|
|
+ return [
|
|
|
+ part.strip("{}")
|
|
|
+ for part in split_by_curly_brackets(cmd_example)
|
|
|
+ if part.startswith("{{") and part.endswith("}}")
|
|
|
+ ]
|
|
|
+
|
|
|
+
|
|
|
+def place_placeholders(cmd_example: str, placeholders: list[str]) -> str:
|
|
|
+ return reduce(
|
|
|
+ lambda cmd, ph: cmd.replace("{{}}", "{{" + ph + "}}", 1),
|
|
|
+ placeholders,
|
|
|
+ cmd_example,
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+def remove_placeholders(cmd_example: str) -> str:
|
|
|
+ return re.sub(r"\{\{.*?\}\}", "{{}}", cmd_example)
|
|
|
+
|
|
|
+
|
|
|
+def add_backticks(cmd_example: str) -> str:
|
|
|
+ return "`" + cmd_example.strip("`") + "`"
|
|
|
+
|
|
|
+
|
|
|
+def update_page(
|
|
|
+ page_path: Path,
|
|
|
+ old_common_part: str,
|
|
|
+ new_common_part: str,
|
|
|
+ dry_run: bool,
|
|
|
+) -> None:
|
|
|
+ with page_path.open("r", encoding="utf-8") as file:
|
|
|
+ page_text = file.read()
|
|
|
+
|
|
|
+ logger.info(f"Processing page: {page_path}")
|
|
|
+
|
|
|
+ cmd_example = find_cmd_example_with_common_part(old_common_part, page_text)
|
|
|
+
|
|
|
+ if not cmd_example:
|
|
|
+ logger.warning(f"Common part '{old_common_part}' not found in '{page_path}'.")
|
|
|
+ return False
|
|
|
+
|
|
|
+ logger.info(f"Found command example: {cmd_example}")
|
|
|
+ new_cmd_example = add_backticks(
|
|
|
+ place_placeholders(new_common_part, parse_placeholders(cmd_example))
|
|
|
+ )
|
|
|
+ logger.info(f"{cmd_example} -> {new_cmd_example}")
|
|
|
+ if not dry_run:
|
|
|
+ new_page_text = page_text.replace(cmd_example, new_cmd_example)
|
|
|
+
|
|
|
+ with page_path.open("w", encoding="utf-8") as file:
|
|
|
+ file.write(new_page_text)
|
|
|
+ return True
|
|
|
+
|
|
|
+
|
|
|
+def parse_arguments() -> argparse.Namespace:
|
|
|
+ parser = argparse.ArgumentParser(description="Update tldr pages.")
|
|
|
+ parser.add_argument(
|
|
|
+ "platform", help="Relative path to the page from the repository root"
|
|
|
+ )
|
|
|
+ parser.add_argument("filename", help="Page file name (without .md)")
|
|
|
+ parser.add_argument(
|
|
|
+ "-c", "--common-part", help="Common part to be modified", required=False
|
|
|
+ )
|
|
|
+ parser.add_argument(
|
|
|
+ "-u", "--updated-common-part", help="Updated common part", required=False
|
|
|
+ )
|
|
|
+ parser.add_argument(
|
|
|
+ "-n",
|
|
|
+ "--dry-run",
|
|
|
+ action="store_true",
|
|
|
+ help="Show what changes would be made without actually modifying the pages",
|
|
|
+ )
|
|
|
+ parser.add_argument(
|
|
|
+ "-v",
|
|
|
+ "--verbose",
|
|
|
+ action="count",
|
|
|
+ default=0,
|
|
|
+ help="Increase verbosity level (use -v, -vv)",
|
|
|
+ )
|
|
|
+
|
|
|
+ args = parser.parse_args()
|
|
|
+
|
|
|
+ if args.verbose > 0:
|
|
|
+ log_levels = [logging.WARNING, logging.INFO]
|
|
|
+ log_level = log_levels[min(args.verbose, len(log_levels) - 1)]
|
|
|
+ else:
|
|
|
+ log_level = logging.ERROR
|
|
|
+
|
|
|
+ logging.basicConfig(level=log_level)
|
|
|
+
|
|
|
+ return args
|
|
|
+
|
|
|
+
|
|
|
+def update_pages(
|
|
|
+ tldr_root: str,
|
|
|
+ platform: str,
|
|
|
+ filename: str,
|
|
|
+ locales: list[str],
|
|
|
+ old_common_part: str,
|
|
|
+ updated_common_part: str,
|
|
|
+ dry_run: bool,
|
|
|
+) -> None:
|
|
|
+ for locale in locales:
|
|
|
+ page_path = get_page_path(tldr_root, locale, platform, filename)
|
|
|
+ if page_path.exists() and page_path.is_file():
|
|
|
+ exists = update_page(
|
|
|
+ page_path,
|
|
|
+ old_common_part,
|
|
|
+ updated_common_part,
|
|
|
+ dry_run,
|
|
|
+ )
|
|
|
+ if not exists and locale == "":
|
|
|
+ logger.warning(
|
|
|
+ f"Common part '{old_common_part}' not found in '{page_path}'."
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+def clean_cmd_example(cmd_example: str) -> str:
|
|
|
+ return remove_placeholders(cmd_example).strip("`")
|
|
|
+
|
|
|
+
|
|
|
+def get_tldr_root() -> Path:
|
|
|
+ f = Path("update-command.py").resolve()
|
|
|
+ return next(path for path in f.parents if path.name == "tldr")
|
|
|
+
|
|
|
+ if "TLDR_ROOT" in os.environ:
|
|
|
+ return Path(os.environ["TLDR_ROOT"])
|
|
|
+ logger.error(
|
|
|
+ "Please set TLDR_ROOT to the location of a clone of https://github.com/tldr-pages/tldr."
|
|
|
+ )
|
|
|
+ sys.exit(1)
|
|
|
+
|
|
|
+
|
|
|
+def main():
|
|
|
+ args = parse_arguments()
|
|
|
+
|
|
|
+ print(
|
|
|
+ "Enter the command examples (any content between double curly brackets will be ignored):"
|
|
|
+ )
|
|
|
+ common_part = (
|
|
|
+ args.common_part
|
|
|
+ if args.common_part
|
|
|
+ else clean_cmd_example(input("Enter the common part to modify: "))
|
|
|
+ )
|
|
|
+ updated_common_part = (
|
|
|
+ args.updated_common_part
|
|
|
+ if args.updated_common_part
|
|
|
+ else clean_cmd_example(input("Enter the change to be made: "))
|
|
|
+ )
|
|
|
+
|
|
|
+ tldr_root = get_tldr_root()
|
|
|
+ locales = [""]
|
|
|
+ locales.extend(get_locales(tldr_root))
|
|
|
+
|
|
|
+ update_pages(
|
|
|
+ tldr_root,
|
|
|
+ args.platform,
|
|
|
+ args.filename + ".md",
|
|
|
+ locales,
|
|
|
+ common_part,
|
|
|
+ updated_common_part,
|
|
|
+ args.dry_run,
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ main()
|