Merge pull request 'feature: add verbose flag, improve command output, improve error display' (#64) from feature/improve-verbose-output into main
Publish to PyPI / Bump-Build-Publish (push) Successful in 45s

Reviewed-on: #64
This commit was merged in pull request #64.
This commit is contained in:
jt
2026-09-26 20:19:41 +00:00
4 changed files with 93 additions and 17 deletions
+3
View File
@@ -62,6 +62,9 @@ más información):
`--remote`), dejando un reporte CSV (`branch-clean-report.csv`) con nombre, commit, autor y
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:
1. Sobre la primer rama principal configurada (por ejemplo, `dev`), se crea una nueva rama con
+30 -10
View File
@@ -1,6 +1,10 @@
import subprocess
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.io import *
@@ -180,10 +184,13 @@ class Git:
self._run(**kwargs)
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:
self._print_process(process.stdout, process.stderr, print)
# Sin --verbose solo se muestra el paso; si falla, la salida la muestra main()
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:
process.check_returncode()
@@ -193,7 +200,7 @@ class Git:
def _run(self, print: str | None = None, check: bool = True):
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 = []
for arg in self.command:
@@ -205,12 +212,25 @@ class Git:
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():
text += "\n" + line
for line in process.stdout.splitlines():
text.append("\n" + line)
for line in stderr.splitlines():
text += "\n[red]" + line + "[/red]"
for line in process.stderr.splitlines():
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
View File
@@ -1,10 +1,19 @@
from rich.console import Console
from rich.prompt import Prompt, Confirm
from rich.panel import Panel
from rich.prompt import Confirm, Prompt
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):
console.print(f":white_check_mark: [green]{msg}[/green]")
+50 -6
View File
@@ -1,12 +1,16 @@
#!/usr/bin/env python3
import traceback
from subprocess import SubprocessError
from subprocess import CalledProcessError, SubprocessError
from typing import Annotated
import click
import typer
from rich.markup import escape
from rich.traceback import Traceback
from git_flow import GitFlowError
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"
LOCALE = "es_AR.UTF-8"
@@ -22,15 +26,55 @@ app.add_typer(release.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():
try:
app()
print()
except GitFlowError as e:
lines = [str(e), ""]
lines += traceback.format_exception(e)
base.io_error("".join(lines), title="Git Flow")
_print_error(escape(str(e)), "Git Flow", e)
except CalledProcessError as e:
_print_error(_format_process_error(e), "Comando", e)
except SubprocessError as e:
base.io_error(str(e), title="Comando")
_print_error(escape(str(e)), "Comando", e)
except KeyboardInterrupt:
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,
)