mirror of
https://github.com/tldr-pages/tldr.git
synced 2024-09-11 03:55:31 +03:00
tooling: add update-command script (#11974)
* 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>
This commit is contained in:
parent
eaea464e9a
commit
561a364e0d
@ -16,6 +16,7 @@ This section contains a summary of the scripts available in this directory. For
|
||||
- [set-more-info-link.py](set-more-info-link.py) is a Python script to generate or update more information links across pages.
|
||||
- [test.sh](test.sh) script runs some basic tests on every PR/commit to make sure that the pages are valid and that the code is formatted correctly.
|
||||
- [wrong-filename.sh](wrong-filename.sh) script checks the consistency between the filenames and the page title.
|
||||
- [update-command.py](update-command.py) is a Python script to update the common contents of a command example across all languages.
|
||||
|
||||
## Compatibility
|
||||
|
||||
@ -29,3 +30,4 @@ The below table shows the compatibility of user-executable scripts with differen
|
||||
| [set-alias-pages.py](set-alias-pages.py) | ✅ | ✅ | ✅ |
|
||||
| [set-more-info-link.py](set-more-info-link.py) | ✅ | ✅ | ✅ |
|
||||
| [wrong-filename.sh](wrong-filename.sh) | ✅ | ❌ | ❌ |
|
||||
| [update-command.py](update-command.py) | ✅ | ✅ | ✅ |
|
||||
|
273
scripts/update-command.py
Executable file
273
scripts/update-command.py
Executable file
@ -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()
|
Loading…
Reference in New Issue
Block a user