This commit is contained in:
Iliyan Angelov
2025-12-01 06:50:10 +02:00
parent 91f51bc6fe
commit 62c1fe5951
4682 changed files with 544807 additions and 31208 deletions

View File

@@ -0,0 +1,967 @@
import logging
import subprocess
import sys
from collections import defaultdict
from enum import Enum
from functools import wraps
import time
from typing import (
TYPE_CHECKING,
Any,
DefaultDict,
Dict,
List,
Optional,
Tuple,
Union,
cast,
)
import click
import typer
from rich.console import Console
from rich.table import Table
from rich.text import Text
from typer.core import MarkupMode, TyperCommand, TyperGroup
from click.utils import make_str
from safety.constants import (
BETA_PANEL_DESCRIPTION_HELP,
MSG_NO_AUTHD_CICD_PROD_STG,
MSG_NO_AUTHD_CICD_PROD_STG_ORG,
MSG_NO_AUTHD_DEV_STG,
MSG_NO_AUTHD_DEV_STG_ORG_PROMPT,
MSG_NO_AUTHD_DEV_STG_PROMPT,
MSG_NO_AUTHD_NOTE_CICD_PROD_STG_TPL,
MSG_NO_VERIFIED_EMAIL_TPL,
CONTEXT_COMMAND_TYPE,
FeatureType,
)
from safety.scan.constants import CONSOLE_HELP_THEME
from safety.models import SafetyCLI
if TYPE_CHECKING:
from click.core import Command, Context
from safety.auth.models import Auth
LOG = logging.getLogger(__name__)
class CommandType(Enum):
MAIN = "main"
UTILITY = "utility"
BETA = "beta"
def custom_print_options_panel(
name: str, params: List[Any], ctx: Any, console: Console
) -> None:
"""
Print a panel with options.
Args:
name (str): The title of the panel.
params (List[Any]): The list of options/arguments to print.
ctx (Any): The context object.
markup_mode (str): The markup mode.
console (Console): The console to print to.
"""
table = Table(title=name, show_lines=True)
for param in params:
opts = getattr(param, "opts", "")
help_text = getattr(param, "help", "")
table.add_row(str(opts), help_text)
console.print(table)
def custom_print_commands_panel(
name: str, commands: List[Any], console: Console
) -> None:
"""
Print a panel with commands.
Args:
name (str): The title of the panel.
commands (List[Any]): The list of commands to print.
console (Console): The console to print to.
"""
table = Table(title=name, show_lines=True)
for command in commands:
table.add_row(command.name, command.help or "")
console.print(table)
def custom_make_rich_text(text: str) -> Text:
"""
Create rich text.
Args:
text (str): The text to format.
Returns:
Text: The formatted rich text.
"""
return Text(text)
def custom_get_help_text(obj: Any) -> Text:
"""
Get the help text for an object.
Args:
obj (Any): The object to get help text for.
Returns:
Text: The formatted help text.
"""
return Text(obj.help)
def custom_make_command_help(help_text: str) -> Text:
"""
Create rich text for command help.
Args:
help_text (str): The help text to format.
markup_mode (str): The markup mode.
Returns:
Text: The formatted rich text.
"""
return Text(help_text)
def get_command_for(name: str, typer_instance: typer.Typer) -> click.Command:
"""
Retrieve a command by name from a Typer instance.
Args:
name (str): The name of the command.
typer_instance (typer.Typer): The Typer instance.
Returns:
click.Command: The found command.
"""
single_command = next(
(
command
for command in typer_instance.registered_commands
if command.name == name
),
None,
)
if not single_command:
raise ValueError("Unable to find the command name.")
single_command.context_settings = typer_instance.info.context_settings
click_command = typer.main.get_command_from_info(
single_command,
pretty_exceptions_short=typer_instance.pretty_exceptions_short,
rich_markup_mode=typer_instance.rich_markup_mode,
)
if typer_instance._add_completion:
click_install_param, click_show_param = (
typer.main.get_install_completion_arguments()
)
click_command.params.append(click_install_param)
click_command.params.append(click_show_param)
return click_command
def pass_safety_cli_obj(func):
"""
Decorator to ensure the SafetyCLI object exists for a command.
"""
@wraps(func)
def inner(ctx, *args, **kwargs):
if not ctx.obj:
ctx.obj = SafetyCLI()
return func(ctx, *args, **kwargs)
return inner
def pretty_format_help(
obj: Union[click.Command, click.Group], ctx: click.Context, markup_mode: MarkupMode
) -> None:
"""
Format and print help text in a pretty format.
Args:
obj (Union[click.Command, click.Group]): The Click command or group.
ctx (click.Context): The Click context.
markup_mode (MarkupMode): The markup mode.
"""
from rich.align import Align
from rich.console import Console
from rich.padding import Padding
from rich.theme import Theme
from typer.rich_utils import (
ARGUMENTS_PANEL_TITLE,
COMMANDS_PANEL_TITLE,
OPTIONS_PANEL_TITLE,
STYLE_USAGE_COMMAND,
highlighter,
)
console = Console()
with console.use_theme(Theme(styles=CONSOLE_HELP_THEME)) as theme_context:
console = theme_context.console
# Print command / group help if we have some
if obj.help:
console.print()
# Print with some padding
console.print(
Padding(Align(custom_get_help_text(obj=obj), pad=False), (0, 1, 0, 1))
)
# Print usage
console.print(
Padding(highlighter(obj.get_usage(ctx)), 1), style=STYLE_USAGE_COMMAND
)
if isinstance(obj, click.MultiCommand):
panel_to_commands: DefaultDict[str, List[click.Command]] = defaultdict(list)
for command_name in obj.list_commands(ctx):
command = obj.get_command(ctx, command_name)
if command and not command.hidden:
panel_name = (
getattr(command, "rich_help_panel", None)
or COMMANDS_PANEL_TITLE
)
panel_to_commands[panel_name].append(command)
# Print each command group panel
default_commands = panel_to_commands.get(COMMANDS_PANEL_TITLE, [])
custom_print_commands_panel(
name=COMMANDS_PANEL_TITLE,
commands=default_commands,
console=console,
)
for panel_name, commands in panel_to_commands.items():
if panel_name == COMMANDS_PANEL_TITLE:
# Already printed above
continue
custom_print_commands_panel(
name=panel_name,
commands=commands,
console=console,
)
panel_to_arguments: DefaultDict[str, List[click.Argument]] = defaultdict(list)
panel_to_options: DefaultDict[str, List[click.Option]] = defaultdict(list)
for param in obj.get_params(ctx):
# Skip if option is hidden
if getattr(param, "hidden", False):
continue
if isinstance(param, click.Argument):
panel_name = (
getattr(param, "rich_help_panel", None) or ARGUMENTS_PANEL_TITLE
)
panel_to_arguments[panel_name].append(param)
elif isinstance(param, click.Option):
panel_name = (
getattr(param, "rich_help_panel", None) or OPTIONS_PANEL_TITLE
)
panel_to_options[panel_name].append(param)
default_options = panel_to_options.get(OPTIONS_PANEL_TITLE, [])
custom_print_options_panel(
name=OPTIONS_PANEL_TITLE,
params=default_options,
ctx=ctx,
console=console,
)
for panel_name, options in panel_to_options.items():
if panel_name == OPTIONS_PANEL_TITLE:
# Already printed above
continue
custom_print_options_panel(
name=panel_name,
params=options,
ctx=ctx,
console=console,
)
default_arguments = panel_to_arguments.get(ARGUMENTS_PANEL_TITLE, [])
custom_print_options_panel(
name=ARGUMENTS_PANEL_TITLE,
params=default_arguments,
ctx=ctx,
console=console,
)
for panel_name, arguments in panel_to_arguments.items():
if panel_name == ARGUMENTS_PANEL_TITLE:
# Already printed above
continue
custom_print_options_panel(
name=panel_name,
params=arguments,
ctx=ctx,
console=console,
)
if ctx.parent:
params = []
for param in ctx.parent.command.params:
if isinstance(param, click.Option):
params.append(param)
custom_print_options_panel(
name="Global-Options",
params=params,
ctx=ctx.parent,
console=console,
)
# Epilogue if we have it
if obj.epilog:
# Remove single linebreaks, replace double with single
lines = obj.epilog.split("\n\n")
epilogue = "\n".join([x.replace("\n", " ").strip() for x in lines])
epilogue_text = custom_make_rich_text(text=epilogue)
console.print(Padding(Align(epilogue_text, pad=False), 1))
def print_main_command_panels(
*,
name: str,
commands_type: CommandType,
commands: List[click.Command],
markup_mode: MarkupMode,
console,
) -> None:
"""
Print the main command panels.
Args:
name (str): The name of the panel.
commands (List[click.Command]): List of commands to display.
markup_mode (MarkupMode): The markup mode.
console: The Rich console.
"""
from rich import box
from rich.panel import Panel
from rich.table import Table
from rich.text import Text
from typer.rich_utils import (
ALIGN_COMMANDS_PANEL,
STYLE_COMMANDS_PANEL_BORDER,
STYLE_COMMANDS_TABLE_BORDER_STYLE,
STYLE_COMMANDS_TABLE_BOX,
STYLE_COMMANDS_TABLE_LEADING,
STYLE_COMMANDS_TABLE_PAD_EDGE,
STYLE_COMMANDS_TABLE_PADDING,
STYLE_COMMANDS_TABLE_ROW_STYLES,
STYLE_COMMANDS_TABLE_SHOW_LINES,
)
t_styles: Dict[str, Any] = {
"show_lines": STYLE_COMMANDS_TABLE_SHOW_LINES,
"leading": STYLE_COMMANDS_TABLE_LEADING,
"box": STYLE_COMMANDS_TABLE_BOX,
"border_style": STYLE_COMMANDS_TABLE_BORDER_STYLE,
"row_styles": STYLE_COMMANDS_TABLE_ROW_STYLES,
"pad_edge": STYLE_COMMANDS_TABLE_PAD_EDGE,
"padding": STYLE_COMMANDS_TABLE_PADDING,
}
box_style = getattr(box, t_styles.pop("box"), None)
commands_table = Table(
highlight=False,
show_header=False,
expand=True,
box=box_style,
**t_styles,
)
console_width = 80
column_width = 25
if console.size and console.size[0] > 80:
console_width = console.size[0]
from rich.console import Group
description = None
if commands_type is CommandType.BETA:
description = Group(Text(""), Text(BETA_PANEL_DESCRIPTION_HELP), Text(""))
commands_table.add_column(
style="bold cyan", no_wrap=True, width=column_width, max_width=column_width
)
commands_table.add_column(width=console_width - column_width)
rows = []
for command in commands:
helptext = command.short_help or command.help or ""
command_name = command.name or ""
command_name_text = (
Text(command_name, style="")
if commands_type is CommandType.BETA
else Text(command_name)
)
rows.append(
[
command_name_text,
custom_make_command_help(
help_text=helptext,
),
]
)
rows.append([])
for row in rows:
commands_table.add_row(*row)
if commands_table.row_count:
renderables = (
[description, commands_table]
if description is not None
else [Text(""), commands_table]
)
console.print(
Panel(
Group(*renderables),
border_style=STYLE_COMMANDS_PANEL_BORDER,
title=name,
title_align=ALIGN_COMMANDS_PANEL,
)
)
# The help output for the main safety root command: `safety --help`
def format_main_help(
obj: Union[click.Command, click.Group], ctx: click.Context, markup_mode: MarkupMode
) -> None:
"""
Format the main help output for the safety root command.
Args:
obj (Union[click.Command, click.Group]): The Click command or group.
ctx (click.Context): The Click context.
markup_mode (MarkupMode): The markup mode.
"""
from rich.align import Align
from rich.console import Console
from rich.padding import Padding
from rich.theme import Theme
from typer.rich_utils import (
ARGUMENTS_PANEL_TITLE,
COMMANDS_PANEL_TITLE,
OPTIONS_PANEL_TITLE,
STYLE_USAGE_COMMAND,
highlighter,
)
typer_console = Console()
with typer_console.use_theme(Theme(styles=CONSOLE_HELP_THEME)) as theme_context:
console = theme_context.console
# Print command / group help if we have some
if obj.help:
console.print()
# Print with some padding
console.print(
Padding(
Align(
custom_get_help_text(obj=obj),
pad=False,
),
(0, 1, 0, 1),
)
)
# Print usage
console.print(
Padding(highlighter(obj.get_usage(ctx)), 1), style=STYLE_USAGE_COMMAND
)
if isinstance(obj, click.MultiCommand):
UTILITY_COMMANDS_PANEL_TITLE = "Utility commands"
BETA_COMMANDS_PANEL_TITLE = "Beta Commands"
COMMANDS_PANEL_TITLE_CONSTANTS = {
CommandType.MAIN: COMMANDS_PANEL_TITLE,
CommandType.UTILITY: UTILITY_COMMANDS_PANEL_TITLE,
CommandType.BETA: BETA_COMMANDS_PANEL_TITLE,
}
panel_to_commands: Dict[CommandType, List[click.Command]] = {}
# Keep order of panels
for command_type in COMMANDS_PANEL_TITLE_CONSTANTS.keys():
panel_to_commands[command_type] = []
for command_name in obj.list_commands(ctx):
command = obj.get_command(ctx, command_name)
if command and not command.hidden:
command_type = command.context_settings.get(
CONTEXT_COMMAND_TYPE, CommandType.MAIN
)
panel_to_commands[command_type].append(command)
for command_type, commands in panel_to_commands.items():
print_main_command_panels(
name=COMMANDS_PANEL_TITLE_CONSTANTS[command_type],
commands_type=command_type,
commands=commands,
markup_mode=markup_mode,
console=console,
)
panel_to_arguments: DefaultDict[str, List[click.Argument]] = defaultdict(list)
panel_to_options: DefaultDict[str, List[click.Option]] = defaultdict(list)
for param in obj.get_params(ctx):
# Skip if option is hidden
if getattr(param, "hidden", False):
continue
if isinstance(param, click.Argument):
panel_name = (
getattr(param, "rich_help_panel", None) or ARGUMENTS_PANEL_TITLE
)
panel_to_arguments[panel_name].append(param)
elif isinstance(param, click.Option):
panel_name = (
getattr(param, "rich_help_panel", None) or OPTIONS_PANEL_TITLE
)
panel_to_options[panel_name].append(param)
default_arguments = panel_to_arguments.get(ARGUMENTS_PANEL_TITLE, [])
custom_print_options_panel(
name=ARGUMENTS_PANEL_TITLE,
params=default_arguments,
ctx=ctx,
console=console,
)
for panel_name, arguments in panel_to_arguments.items():
if panel_name == ARGUMENTS_PANEL_TITLE:
# Already printed above
continue
custom_print_options_panel(
name=panel_name,
params=arguments,
ctx=ctx,
console=console,
)
default_options = panel_to_options.get(OPTIONS_PANEL_TITLE, [])
custom_print_options_panel(
name=OPTIONS_PANEL_TITLE,
params=default_options,
ctx=ctx,
console=console,
)
for panel_name, options in panel_to_options.items():
if panel_name == OPTIONS_PANEL_TITLE:
# Already printed above
continue
custom_print_options_panel(
name=panel_name,
params=options,
ctx=ctx,
console=console,
)
# Epilogue if we have it
if obj.epilog:
# Remove single linebreaks, replace double with single
lines = obj.epilog.split("\n\n")
epilogue = "\n".join([x.replace("\n", " ").strip() for x in lines])
epilogue_text = custom_make_rich_text(text=epilogue)
console.print(Padding(Align(epilogue_text, pad=False), 1))
def process_auth_status_not_ready(console, auth: "Auth", ctx: typer.Context) -> None:
"""
Handle the process when the authentication status is not ready.
Args:
console: The Rich console.
auth (Auth): The Auth object.
ctx (typer.Context): The Typer context.
"""
from rich.prompt import Confirm, Prompt
from safety_schemas.models import Stage
from safety.auth.constants import CLI_AUTH, MSG_NON_AUTHENTICATED
if not auth.client or not auth.client.is_using_auth_credentials():
if auth.stage is Stage.development:
console.print()
if auth.org:
confirmed = Confirm.ask(
MSG_NO_AUTHD_DEV_STG_ORG_PROMPT,
choices=["Y", "N", "y", "n"],
show_choices=False,
show_default=False,
default=True,
console=console,
)
if not confirmed:
sys.exit(0)
from safety.auth.cli import auth_app
login_command = get_command_for(name="login", typer_instance=auth_app)
ctx.invoke(login_command)
else:
console.print(MSG_NO_AUTHD_DEV_STG)
console.print()
choices = ["L", "R", "l", "r"]
next_command = Prompt.ask(
MSG_NO_AUTHD_DEV_STG_PROMPT,
default=None,
choices=choices,
show_choices=False,
console=console,
)
from safety.auth.cli import auth_app
login_command = get_command_for(name="login", typer_instance=auth_app)
register_command = get_command_for(
name="register", typer_instance=auth_app
)
if next_command is None or next_command.lower() not in choices:
sys.exit(0)
console.print()
if next_command.lower() == "r":
ctx.invoke(register_command)
else:
ctx.invoke(login_command)
if not ctx.obj.auth.email_verified:
sys.exit(1)
else:
if not auth.org:
console.print(MSG_NO_AUTHD_CICD_PROD_STG_ORG.format(LOGIN_URL=CLI_AUTH))
else:
console.print(MSG_NO_AUTHD_CICD_PROD_STG)
console.print(
MSG_NO_AUTHD_NOTE_CICD_PROD_STG_TPL.format(
LOGIN_URL=CLI_AUTH, SIGNUP_URL=f"{CLI_AUTH}/?sign_up=True"
)
)
sys.exit(1)
elif not auth.email_verified:
console.print()
console.print(
MSG_NO_VERIFIED_EMAIL_TPL.format(
email=auth.email if auth.email else "Missing email"
)
)
sys.exit(1)
else:
console.print(MSG_NON_AUTHENTICATED)
sys.exit(1)
class CustomContext(click.Context):
def __init__(
self,
command: "Command",
parent: Optional["Context"] = None,
command_type: CommandType = CommandType.MAIN,
feature_type: Optional[FeatureType] = None,
**kwargs,
) -> None:
self.command_type = command_type
self.feature_type = feature_type
self.started_at = time.monotonic()
self.command_alias_used: Optional[str] = None
super().__init__(command, parent=parent, **kwargs)
class SafetyCLISubGroup(TyperGroup):
"""
Custom TyperGroup with additional functionality for Safety CLI.
"""
context_class = CustomContext
def format_help(self, ctx: click.Context, formatter: click.HelpFormatter) -> None:
"""
Format help message with rich formatting.
Args:
ctx (click.Context): Click context.
formatter (click.HelpFormatter): Click help formatter.
"""
pretty_format_help(self, ctx, markup_mode=self.rich_markup_mode)
def format_usage(self, ctx: click.Context, formatter: click.HelpFormatter) -> None:
"""
Format usage message.
Args:
ctx (click.Context): Click context.
formatter (click.HelpFormatter): Click help formatter.
"""
command_path = ctx.command_path
pieces = self.collect_usage_pieces(ctx)
main_group = ctx.parent
if main_group:
command_path = (
f"{main_group.command_path} [GLOBAL-OPTIONS] {ctx.command.name}"
)
formatter.write_usage(command_path, " ".join(pieces))
def command(
self,
*args: Any,
**kwargs: Any,
) -> click.Command: # type: ignore[override]
"""
Create a new command.
Args:
*args: Variable length argument list.
**kwargs: Arbitrary keyword arguments.
Returns:
click.Command: The created command.
"""
super().command(*args, **kwargs)
class SafetyCLICommand(TyperCommand):
"""
Custom TyperCommand with additional functionality for Safety CLI.
"""
context_class = CustomContext
def format_help(self, ctx: click.Context, formatter: click.HelpFormatter) -> None:
"""
Format help message with rich formatting.
Args:
ctx (click.Context): Click context.
formatter (click.HelpFormatter): Click help formatter.
"""
pretty_format_help(self, ctx, markup_mode=self.rich_markup_mode)
def format_usage(self, ctx: click.Context, formatter: click.HelpFormatter) -> None:
"""
Format usage message.
Args:
ctx (click.Context): Click context.
formatter (click.HelpFormatter): Click help formatter.
"""
command_path = ctx.command_path
pieces = self.collect_usage_pieces(ctx)
main_group = ctx.parent
if main_group:
command_path = (
f"{main_group.command_path} [GLOBAL-OPTIONS] {ctx.command.name}"
)
formatter.write_usage(command_path, " ".join(pieces))
class SafetyCLILegacyGroup(click.Group):
"""
Custom Click Group to handle legacy command-line arguments.
"""
context_class = CustomContext
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.all_commands = {}
def __call__(self, *args, **kwargs):
return super().__call__(*args, **kwargs)
def add_command(self, cmd, name=None) -> None:
super().add_command(cmd, name)
name = name or cmd.name
self.all_commands[name] = cmd
def parse_args(self, ctx: click.Context, args: List[str]) -> List[str]:
ctx = cast(CustomContext, ctx)
if len(args) >= 1:
if "pip" in args[0] and ctx:
ctx.command_alias_used = args[0]
args[0] = "pip"
parsed_args = super().parse_args(ctx, args)
args = ctx.args
# Workaround for legacy check options, that now are global options
subcommand_args = set(args)
PROXY_HOST_OPTIONS = set(["--proxy-host", "-ph"])
if (
"check" in ctx.protected_args
or "license" in ctx.protected_args
and (
bool(
PROXY_HOST_OPTIONS.intersection(subcommand_args)
or "--key" in subcommand_args
)
)
):
proxy_options, key = self.parse_legacy_args(args)
if proxy_options:
ctx.params.update(proxy_options)
if key:
ctx.params.update({"key": key})
return parsed_args
def parse_legacy_args(
self, args: List[str]
) -> Tuple[Optional[Dict[str, str]], Optional[str]]:
"""
Parse legacy command-line arguments for proxy settings and keys.
Args:
args (List[str]): List of command-line arguments.
Returns:
Tuple[Optional[Dict[str, str]], Optional[str]]: Parsed proxy options and key.
"""
options = {"proxy_protocol": "https", "proxy_port": 80, "proxy_host": None}
key = None
for i, arg in enumerate(args):
if arg in ["--proxy-protocol", "-pr"] and i + 1 < len(args):
options["proxy_protocol"] = args[i + 1]
elif arg in ["--proxy-port", "-pp"] and i + 1 < len(args):
options["proxy_port"] = int(args[i + 1])
elif arg in ["--proxy-host", "-ph"] and i + 1 < len(args):
options["proxy_host"] = args[i + 1]
elif arg in ["--key"] and i + 1 < len(args):
key = args[i + 1]
proxy = options if options["proxy_host"] else None
return proxy, key
def get_filtered_commands(self, ctx: click.Context) -> Dict[str, click.Command]:
from safety.auth.utils import initialize
initialize(ctx, refresh=False)
# Filter commands here:
from .constants import CONTEXT_FEATURE_TYPE
disabled_features = [
feature_type
for feature_type in FeatureType
if not getattr(ctx.obj, feature_type.attr_name, False)
]
return {
k: v
for k, v in self.commands.items()
if v.context_settings.get(CONTEXT_FEATURE_TYPE, None)
not in disabled_features
or k in ["firewall"]
}
def invoke(self, ctx: click.Context) -> None:
"""
Invoke the command, handling legacy arguments.
Args:
ctx (click.Context): Click context.
"""
session_kwargs = {
"ctx": ctx,
"proxy_protocol": ctx.params.pop("proxy_protocol", None),
"proxy_host": ctx.params.pop("proxy_host", None),
"proxy_port": ctx.params.pop("proxy_port", None),
"key": ctx.params.pop("key", None),
"stage": ctx.params.pop("stage", None),
}
invoked_command = make_str(next(iter(ctx.protected_args), ""))
from safety.auth.cli_utils import inject_session
inject_session(**session_kwargs, invoked_command=invoked_command)
# call initialize if the --key is used.
if session_kwargs["key"]:
from safety.auth.utils import initialize
initialize(ctx, refresh=True)
self.commands = self.get_filtered_commands(ctx)
# Now, invoke the original behavior
super(SafetyCLILegacyGroup, self).invoke(ctx)
def list_commands(self, ctx: click.Context) -> List[str]:
"""Override click.Group.list_commands with custom filtering"""
self.commands = self.get_filtered_commands(ctx)
return super().list_commands(ctx)
def format_help(self, ctx: click.Context, formatter: click.HelpFormatter) -> None:
"""
Format help message with rich formatting.
Args:
ctx (click.Context): Click context.
formatter (click.HelpFormatter): Click help formatter.
"""
# The main `safety --help`
if self.name == "cli":
format_main_help(self, ctx, markup_mode="rich")
# All other help outputs
else:
pretty_format_help(self, ctx, markup_mode="rich")
class SafetyCLILegacyCommand(click.Command):
"""
Custom Click Command to handle legacy command-line arguments.
"""
context_class = CustomContext
def format_help(self, ctx: click.Context, formatter: click.HelpFormatter) -> None:
"""
Format help message with rich formatting.
Args:
ctx (click.Context): Click context.
formatter (click.HelpFormatter): Click help formatter.
"""
pretty_format_help(self, ctx, markup_mode="rich")
def get_git_branch_name() -> Optional[str]:
"""
Retrieves the current Git branch name.
Returns:
str: The current Git branch name, or None if it cannot be determined.
"""
try:
branch_name = subprocess.check_output(
["git", "rev-parse", "--abbrev-ref", "HEAD"],
stderr=subprocess.DEVNULL,
text=True,
).strip()
return branch_name if branch_name else None
except Exception:
return None