From ffe6e03705d5e5aba4cf79c83a398922b8e64292 Mon Sep 17 00:00:00 2001 From: a-kenji Date: Wed, 26 Jun 2024 22:41:43 +0200 Subject: [PATCH] clan: implement `OSC8` hyperlinks for help output The name of the terminal help output stays the same to keep compatibility with legacy terminal implementations. --- pkgs/clan-cli/clan_cli/__init__.py | 35 ++++++++++---------- pkgs/clan-cli/clan_cli/facts/__init__.py | 21 ++++++------ pkgs/clan-cli/clan_cli/hyperlink.py | 41 ++++++++++++++++++++++++ pkgs/clan-cli/docs.py | 5 +-- 4 files changed, 73 insertions(+), 29 deletions(-) create mode 100644 pkgs/clan-cli/clan_cli/hyperlink.py diff --git a/pkgs/clan-cli/clan_cli/__init__.py b/pkgs/clan-cli/clan_cli/__init__.py index 63d766afd..cbd9b4548 100644 --- a/pkgs/clan-cli/clan_cli/__init__.py +++ b/pkgs/clan-cli/clan_cli/__init__.py @@ -28,6 +28,7 @@ from . import ( from .custom_logger import setup_logging from .dirs import get_clan_flake_toplevel_or_env from .errors import ClanCmdError, ClanError +from .hyperlink import help_hyperlink from .profiler import profile from .ssh import cli as ssh_cli @@ -89,9 +90,9 @@ def create_parser(prog: str | None = None) -> argparse.ArgumentParser: prog=prog, description="The clan cli tool.", epilog=( - """ -Online reference for the clan cli tool: https://docs.clan.lol/reference/cli/ -For more detailed information, visit: https://docs.clan.lol + f""" +Online reference for the clan cli tool: {help_hyperlink("cli reference", "https://docs.clan.lol/reference/cli/")} +For more detailed information, visit: {help_hyperlink("docs", "https://docs.clan.lol")} """ ), formatter_class=argparse.RawTextHelpFormatter, @@ -127,7 +128,7 @@ Note: The meta results from clan/meta.json and manual flake arguments. It may no help="manage backups of clan machines", description="manage backups of clan machines", epilog=( - """ + f""" This subcommand provides an interface to backups that clan machines expose. Examples: @@ -142,7 +143,7 @@ Examples: The backup to restore for the machine [MACHINE] with the configured [PROVIDER] with the name [NAME]. -For more detailed information, visit: https://docs.clan.lol/getting-started/backups/ +For more detailed information visit: {help_hyperlink("backups", "https://docs.clan.lol/getting-started/backups")}. """ ), formatter_class=argparse.RawTextHelpFormatter, @@ -154,13 +155,13 @@ For more detailed information, visit: https://docs.clan.lol/getting-started/back help="create a clan flake inside the current directory", description="create a clan flake inside the current directory", epilog=( - """ + f""" Examples: $ clan flakes create [DIR] Will create a new clan flake in the specified directory and create it if it doesn't exist yet. The flake will be created from a default template. -For more detailed information, visit: https://docs.clan.lol/getting-started +For more detailed information, visit: {help_hyperlink("getting-started", "https://docs.clan.lol/getting-started")} """ ), formatter_class=argparse.RawTextHelpFormatter, @@ -185,7 +186,7 @@ For more detailed information, visit: https://docs.clan.lol/getting-started help="ssh to a remote machine", description="ssh to a remote machine", epilog=( - """ + f""" This subcommand allows seamless ssh access to the nixos-image builders. Examples: @@ -195,7 +196,7 @@ Examples: the json string. [JSON] can either be a json formatted string itself, or point towards a file containing the deployment information -For more detailed information, visit: https://docs.clan.lol/getting-started/deploy +For more detailed information, visit: {help_hyperlink("deploy", "https://docs.clan.lol/getting-started/deploy")} """ ), formatter_class=argparse.RawTextHelpFormatter, @@ -207,7 +208,7 @@ For more detailed information, visit: https://docs.clan.lol/getting-started/depl help="manage secrets", description="manage secrets", epilog=( - """ + f""" This subcommand provides an interface to secret facts. Examples: @@ -219,7 +220,7 @@ Examples: $ clan secrets get [SECRET] Will display the content of the specified secret. -For more detailed information, visit: https://docs.clan.lol/getting-started/secrets/ +For more detailed information, visit: {help_hyperlink("secrets", "https://docs.clan.lol/getting-started/secrets")} """ ), formatter_class=argparse.RawTextHelpFormatter, @@ -231,7 +232,7 @@ For more detailed information, visit: https://docs.clan.lol/getting-started/secr help="manage facts", description="manage facts", epilog=( - """ + f""" This subcommand provides an interface to facts of clan machines. Facts are artifacts that a service can generate. @@ -256,7 +257,7 @@ Examples: This is especially useful for resetting certain passwords while leaving the rest of the facts for a machine in place. -For more detailed information, visit: https://docs.clan.lol/getting-started/secrets/ +For more detailed information, visit: {help_hyperlink("secrets", "https://docs.clan.lol/getting-started/secrets")} """ ), formatter_class=argparse.RawTextHelpFormatter, @@ -268,7 +269,7 @@ For more detailed information, visit: https://docs.clan.lol/getting-started/secr help="manage machines and their configuration", description="manage machines and their configuration", epilog=( - """ + f""" This subcommand provides an interface to machines managed by clan. Examples: @@ -283,7 +284,7 @@ Examples: $ clan machines install [MACHINES] [TARGET_HOST] Will install the specified machine [MACHINE], to the specified [TARGET_HOST]. -For more detailed information, visit: https://docs.clan.lol/getting-started/deploy +For more detailed information, visit: {help_hyperlink("deploy", "https://docs.clan.lol/deploy")} """ ), formatter_class=argparse.RawTextHelpFormatter, @@ -314,7 +315,7 @@ For more detailed information, visit: https://docs.clan.lol/getting-started/depl help="query state information about machines", description="query state information about machines", epilog=( - """ + f""" This subcommand provides an interface to the state managed by clan. State can be folders and databases that modules depend on managed by clan. @@ -334,7 +335,7 @@ Examples: $ clan state list [MACHINE] List state of the machines managed by clan. -For more detailed information, visit: https://docs.clan.lol/getting-started/backups +For more detailed information, visit: {help_hyperlink("getting-started", "https://docs.clan.lol/backups")} """ ), formatter_class=argparse.RawTextHelpFormatter, diff --git a/pkgs/clan-cli/clan_cli/facts/__init__.py b/pkgs/clan-cli/clan_cli/facts/__init__.py index d42c92f02..4a344f4ff 100644 --- a/pkgs/clan-cli/clan_cli/facts/__init__.py +++ b/pkgs/clan-cli/clan_cli/facts/__init__.py @@ -1,6 +1,7 @@ # !/usr/bin/env python3 import argparse +from ..hyperlink import help_hyperlink from .check import register_check_parser from .generate import register_generate_parser from .list import register_list_parser @@ -20,7 +21,7 @@ def register_parser(parser: argparse.ArgumentParser) -> None: "check", help="check if facts are up to date", epilog=( - """ + f""" This subcommand allows checking if all facts are up to date. Examples: @@ -29,7 +30,7 @@ Examples: Will check facts for the specified machine. -For more detailed information, visit: https://docs.clan.lol/getting-started/secrets/ +For more detailed information, visit: {help_hyperlink("secrets", "https://docs.clan.lol/getting-started/secrets")} """ ), formatter_class=argparse.RawTextHelpFormatter, @@ -40,7 +41,7 @@ For more detailed information, visit: https://docs.clan.lol/getting-started/secr "list", help="list all facts", epilog=( - """ + f""" This subcommand allows listing all public facts for a specific machine. The resulting list will be a json string with the name of the fact as its key @@ -48,9 +49,9 @@ and the fact itself as it's value. This is how an example output might look like: ``` -{ +\u007b "[FACT_NAME]": "[FACT]" -} +\u007d ``` Examples: @@ -59,7 +60,7 @@ Examples: Will list facts for the specified machine. -For more detailed information, visit: https://docs.clan.lol/getting-started/secrets/ +For more detailed information, visit: {help_hyperlink("secrets", "https://docs.clan.lol/getting-started/secrets")} """ ), formatter_class=argparse.RawTextHelpFormatter, @@ -70,7 +71,7 @@ For more detailed information, visit: https://docs.clan.lol/getting-started/secr "generate", help="generate public and secret facts for machines", epilog=( - """ + f""" This subcommand allows control of the generation of facts. Often this function will be invoked automatically on deploying machines, but there are situations the user may want to have more granular control, @@ -99,7 +100,7 @@ Examples: This is especially useful for resetting certain passwords while leaving the rest of the facts for a machine in place. -For more detailed information, visit: https://docs.clan.lol/getting-started/secrets/ +For more detailed information, visit: {help_hyperlink("secrets", "https://docs.clan.lol/getting-started/secrets")} """ ), formatter_class=argparse.RawTextHelpFormatter, @@ -110,7 +111,7 @@ For more detailed information, visit: https://docs.clan.lol/getting-started/secr "upload", help="upload secrets for machines", epilog=( - """ + f""" This subcommand allows uploading secrets to remote machines. If using sops as a secret backend it will upload the private key to the machine. @@ -123,7 +124,7 @@ Examples: $ clan facts upload [MACHINE] Will upload secrets to a specific machine. -For more detailed information, visit: https://docs.clan.lol/getting-started/secrets/ +For more detailed information, visit: {help_hyperlink("secrets", "https://docs.clan.lol/getting-started/secrets")} """ ), formatter_class=argparse.RawTextHelpFormatter, diff --git a/pkgs/clan-cli/clan_cli/hyperlink.py b/pkgs/clan-cli/clan_cli/hyperlink.py new file mode 100644 index 000000000..d3f3e94ee --- /dev/null +++ b/pkgs/clan-cli/clan_cli/hyperlink.py @@ -0,0 +1,41 @@ +# Implementation of OSC8 +def hyperlink(text: str, url: str) -> str: + """ + Generate OSC8 escape sequence for hyperlinks. + + Args: + url (str): The URL to link to. + text (str): The text to display. + + Returns: + str: The formatted string with an embedded hyperlink. + """ + esc = "\033" + return f"{esc}]8;;{url}{esc}\\{text}{esc}]8;;{esc}\\" + + +def hyperlink_same_text_and_url(url: str) -> str: + """ + Keep the description and the link the same to support legacy terminals. + """ + return hyperlink(url, url) + + +def help_hyperlink(description: str, url: str) -> str: + import sys + + """ + Keep the description and the link the same to support legacy terminals. + """ + if sys.argv[0].__contains__("docs.py"): + return docs_hyperlink(description, url) + + else: + return hyperlink_same_text_and_url(url) + + +def docs_hyperlink(description: str, url: str) -> str: + """ + Returns a markdown hyperlink + """ + return f"[{description}]({url})" diff --git a/pkgs/clan-cli/docs.py b/pkgs/clan-cli/docs.py index cf342b8a9..f2a01dd55 100644 --- a/pkgs/clan-cli/docs.py +++ b/pkgs/clan-cli/docs.py @@ -105,8 +105,9 @@ def epilog_to_md(text: str) -> str: md += "\n" md += "\n" else: - if contains_https_link(line): - line = convert_to_markdown_link(line) + # TODO: check, if the link is already a markdown link, only convert if not + # if contains_https_link(line): + # line = convert_to_markdown_link(line) md += line md += "\n" else: