feature: add verbose flag, improve command output, improve error display #64
@@ -62,6 +62,9 @@ más información):
|
|||||||
`--remote`), dejando un reporte CSV (`branch-clean-report.csv`) con nombre, commit, autor y
|
`--remote`), dejando un reporte CSV (`branch-clean-report.csv`) con nombre, commit, autor y
|
||||||
fecha de cada rama eliminada.
|
fecha de cada rama eliminada.
|
||||||
|
|
||||||
|
Por defecto solo se muestra cada paso que se ejecuta. Con `git-flow --verbose <command>` (o `-v`) se
|
||||||
|
muestra además la salida de cada comando git y el traceback completo de los errores.
|
||||||
|
|
||||||
El workflow para el cual se penso la herramienta es el siguiente:
|
El workflow para el cual se penso la herramienta es el siguiente:
|
||||||
|
|
||||||
1. Sobre la primer rama principal configurada (por ejemplo, `dev`), se crea una nueva rama con
|
1. Sobre la primer rama principal configurada (por ejemplo, `dev`), se crea una nueva rama con
|
||||||
|
|||||||
+30
-10
@@ -1,6 +1,10 @@
|
|||||||
import subprocess
|
import subprocess
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from rich.markup import escape
|
||||||
|
from rich.panel import Panel
|
||||||
|
from rich.text import Text
|
||||||
|
|
||||||
from git_flow import FLOWCONFIG_FILENAME
|
from git_flow import FLOWCONFIG_FILENAME
|
||||||
from git_flow.io import *
|
from git_flow.io import *
|
||||||
|
|
||||||
@@ -180,10 +184,13 @@ class Git:
|
|||||||
self._run(**kwargs)
|
self._run(**kwargs)
|
||||||
|
|
||||||
def _get(self, print: str | None = None, check: bool = True):
|
def _get(self, print: str | None = None, check: bool = True):
|
||||||
process = subprocess.run(self.command, capture_output=True, text=True)
|
process = subprocess.run(self.command, capture_output=True, text=True, check=False)
|
||||||
|
|
||||||
if print is not None:
|
# Sin --verbose solo se muestra el paso; si falla, la salida la muestra main()
|
||||||
self._print_process(process.stdout, process.stderr, print)
|
if print is not None and settings.verbose:
|
||||||
|
self._print_process(process, print)
|
||||||
|
elif print is not None:
|
||||||
|
console.print(Text.assemble(("› ", "cyan"), print))
|
||||||
|
|
||||||
if check:
|
if check:
|
||||||
process.check_returncode()
|
process.check_returncode()
|
||||||
@@ -193,7 +200,7 @@ class Git:
|
|||||||
def _run(self, print: str | None = None, check: bool = True):
|
def _run(self, print: str | None = None, check: bool = True):
|
||||||
return self._get(print, check)
|
return self._get(print, check)
|
||||||
|
|
||||||
def _print_process(self, stdout: str, stderr: str, title: str):
|
def _print_process(self, process: subprocess.CompletedProcess, title: str | None):
|
||||||
command = []
|
command = []
|
||||||
|
|
||||||
for arg in self.command:
|
for arg in self.command:
|
||||||
@@ -205,12 +212,25 @@ class Git:
|
|||||||
|
|
||||||
command.append(arg)
|
command.append(arg)
|
||||||
|
|
||||||
text = f"[green]$ {' '.join(command)}[/green]"
|
ok = process.returncode == 0
|
||||||
|
# git escribe mensajes informativos en stderr, así que solo se resalta si falló
|
||||||
|
stderr_style = "dim" if ok else "red"
|
||||||
|
text = Text.assemble(("$ ", "dim"), (" ".join(command), "bold yellow"))
|
||||||
|
|
||||||
for line in stdout.splitlines():
|
for line in process.stdout.splitlines():
|
||||||
text += "\n" + line
|
text.append("\n" + line)
|
||||||
|
|
||||||
for line in stderr.splitlines():
|
for line in process.stderr.splitlines():
|
||||||
text += "\n[red]" + line + "[/red]"
|
text.append("\n" + line, stderr_style)
|
||||||
|
|
||||||
panel(title or "Ejecutando", text)
|
console.print(
|
||||||
|
Panel(
|
||||||
|
text,
|
||||||
|
title=f"[bold]{escape(title or 'Ejecutando')}[/bold]",
|
||||||
|
title_align="left",
|
||||||
|
subtitle=None if ok else f"código de salida {process.returncode}",
|
||||||
|
subtitle_align="right",
|
||||||
|
border_style="bright_black" if ok else "red",
|
||||||
|
expand=False,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|||||||
+10
-1
@@ -1,10 +1,19 @@
|
|||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.prompt import Prompt, Confirm
|
|
||||||
from rich.panel import Panel
|
from rich.panel import Panel
|
||||||
|
from rich.prompt import Confirm, Prompt
|
||||||
|
|
||||||
console = Console(highlight=False)
|
console = Console(highlight=False)
|
||||||
|
|
||||||
|
|
||||||
|
class Settings:
|
||||||
|
"""Opciones globales de ejecución, configuradas desde la línea de comandos."""
|
||||||
|
|
||||||
|
verbose: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
settings = Settings()
|
||||||
|
|
||||||
|
|
||||||
def success(msg: str):
|
def success(msg: str):
|
||||||
console.print(f":white_check_mark: [green]{msg}[/green]")
|
console.print(f":white_check_mark: [green]{msg}[/green]")
|
||||||
|
|
||||||
|
|||||||
+50
-6
@@ -1,12 +1,16 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import traceback
|
from subprocess import CalledProcessError, SubprocessError
|
||||||
from subprocess import SubprocessError
|
from typing import Annotated
|
||||||
|
|
||||||
|
import click
|
||||||
import typer
|
import typer
|
||||||
|
from rich.markup import escape
|
||||||
|
from rich.traceback import Traceback
|
||||||
|
|
||||||
from git_flow import GitFlowError
|
from git_flow import GitFlowError
|
||||||
from git_flow.command import base, branch, commit, init, merge, new, release, tag
|
from git_flow.command import base, branch, commit, init, merge, new, release, tag
|
||||||
|
from git_flow.io import console, settings
|
||||||
|
|
||||||
REPOSITORY_TOKEN_PATH = ".repository-token"
|
REPOSITORY_TOKEN_PATH = ".repository-token"
|
||||||
LOCALE = "es_AR.UTF-8"
|
LOCALE = "es_AR.UTF-8"
|
||||||
@@ -22,15 +26,55 @@ app.add_typer(release.app)
|
|||||||
app.add_typer(branch.app)
|
app.add_typer(branch.app)
|
||||||
|
|
||||||
|
|
||||||
|
@app.callback()
|
||||||
|
def options(
|
||||||
|
verbose: Annotated[
|
||||||
|
bool,
|
||||||
|
typer.Option(
|
||||||
|
"--verbose",
|
||||||
|
"-v",
|
||||||
|
help="Muestra la salida de los comandos git y el traceback de los errores",
|
||||||
|
),
|
||||||
|
] = False,
|
||||||
|
):
|
||||||
|
settings.verbose = verbose
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
try:
|
try:
|
||||||
app()
|
app()
|
||||||
print()
|
print()
|
||||||
except GitFlowError as e:
|
except GitFlowError as e:
|
||||||
lines = [str(e), ""]
|
_print_error(escape(str(e)), "Git Flow", e)
|
||||||
lines += traceback.format_exception(e)
|
except CalledProcessError as e:
|
||||||
base.io_error("".join(lines), title="Git Flow")
|
_print_error(_format_process_error(e), "Comando", e)
|
||||||
except SubprocessError as e:
|
except SubprocessError as e:
|
||||||
base.io_error(str(e), title="Comando")
|
_print_error(escape(str(e)), "Comando", e)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
base.io_error("Ejecución abortada")
|
base.io_error("Ejecución abortada")
|
||||||
|
|
||||||
|
|
||||||
|
def _format_process_error(e: CalledProcessError) -> str:
|
||||||
|
command = " ".join(e.cmd) if isinstance(e.cmd, list) else str(e.cmd)
|
||||||
|
message = f"[bold]$ {escape(command)}[/bold]\nTerminó con código de salida {e.returncode}"
|
||||||
|
|
||||||
|
for output in (e.stdout, e.stderr):
|
||||||
|
if isinstance(output, str) and output.strip():
|
||||||
|
message += "\n\n" + escape(output.strip())
|
||||||
|
|
||||||
|
return message
|
||||||
|
|
||||||
|
|
||||||
|
def _print_error(message: str, title: str, e: BaseException):
|
||||||
|
if settings.verbose:
|
||||||
|
base.io_error(message, title=title)
|
||||||
|
console.print(
|
||||||
|
Traceback.from_exception(
|
||||||
|
type(e), e, e.__traceback__, suppress=[click, typer]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
base.io_error(
|
||||||
|
message + "\n\n[dim]Ejecutar con `git-flow --verbose <comando>` para ver el detalle[/dim]",
|
||||||
|
title=title,
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user