mirror of
https://github.com/tldr-pages/tldr.git
synced 2024-09-11 12:05:35 +03:00
* tooling: add update-command script * update-command: add os import Co-authored-by: K.B.Dharun Krishna <kbdharunkrishna@gmail.com> * update-command: remove sync argument and small refactor * update-command: add shebang and license * update-command: remove unused function * update-command: add dry-run option * update-command: add docs in header * update-command: fix old_common_part var name * update-command: require command without .md * update-command: fix command name in description Co-authored-by: Juri Dispan <juri.dispan@posteo.net> * update-command: remove ".md" suffix in description * update-command: enclosing positional parameters with angle brackets Co-authored-by: K.B.Dharun Krishna <kbdharunkrishna@gmail.com> * update-command: enclose paremeters with double angle brackets Co-authored-by: K.B.Dharun Krishna <kbdharunkrishna@gmail.com> * update-command: refine wording Co-authored-by: K.B.Dharun Krishna <kbdharunkrishna@gmail.com> * update-command: remove unused functions * update-command: detect tldr root from any subdir * update-command: call place_placeholders directly * update-command: add summary and compatibility to scripts/README.md * scripts/update-command.py: remove unused method * scripts/update-command.py: use logger.info instead of print * scripts/update-command.py: rename command to cmd_example * scripts/README.md: use command example instead of command * scripts/update-command.py: remove nonexistent optional argument * scripts/update-command.py: improve interactive example * scripts/update-command.py: run black --------- Co-authored-by: K.B.Dharun Krishna <kbdharunkrishna@gmail.com> Co-authored-by: Juri Dispan <juri.dispan@posteo.net> Co-authored-by: Sebastiaan Speck <12570668+sebastiaanspeck@users.noreply.github.com>
274 lines
7.7 KiB
Python
Executable File
274 lines
7.7 KiB
Python
Executable File
#!/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()
|