evita prompts de git interactius i mostra el motiu d'error sencer

This commit is contained in:
2026-05-18 11:52:17 +02:00
parent f35ce3c5dd
commit d22e3885af
+35 -16
View File
@@ -25,7 +25,7 @@ from rich.live import Live
from rich.table import Table from rich.table import Table
from rich.text import Text from rich.text import Text
__version__ = "1.0.4" __version__ = "1.0.5"
console = Console() console = Console()
@@ -301,30 +301,35 @@ def render(entries: list[RepoEntry], cursor: int, base: Path, owner: str, status
# Cursor # Cursor
caret = Text("", style="bold yellow") if i == cursor else Text(" ") caret = Text("", style="bold yellow") if i == cursor else Text(" ")
# Estado # Estat
if e.cloning: if e.cloning:
state = Text(f"{spinner_frame()} clonant…", style="bold blue") state = Text(f"{spinner_frame()} clonant…", style="bold blue")
elif e.cloned_ok is True: elif e.cloned_ok is True:
state = Text("✓ clonado", style="bold green") state = Text("✓ clonat", style="bold green")
elif e.cloned_ok is False: elif e.cloned_ok is False:
state = Text(f"{e.error[:11]}", style="bold red") state = Text("error", style="bold red")
elif e.local_path is not None and e.remote is None: elif e.local_path is not None and e.remote is None:
state = Text("sólo local", style="dim yellow") state = Text("només local", style="dim yellow")
elif e.local_path is not None: elif e.local_path is not None:
state = Text("● en local", style="green") state = Text("● en local", style="green")
elif e.selected: elif e.selected:
state = Text("☑ marcado", style="bold cyan") state = Text("☑ marcat", style="bold cyan")
else: else:
state = Text("○ remoto", style="dim") state = Text("○ remot", style="dim")
# Nombre con marca de privado # Nom amb marca de privat
name = Text(e.name) name = Text(e.name)
if e.remote and e.remote.private: if e.remote and e.remote.private:
name = Text("🔒 ", style="yellow") + name name = Text("🔒 ", style="yellow") + name
if i == cursor: if i == cursor:
name.stylize("bold underline") name.stylize("bold underline")
desc = (e.remote.description if e.remote else "(no está en el servidor)") or "" # Descripció: motiu de l'error si n'hi ha, si no la del repo
if e.cloned_ok is False and e.error:
desc = Text(e.error, style="red")
else:
desc_text = (e.remote.description if e.remote else "(no està al servidor)") or ""
desc = Text(desc_text)
table.add_row(caret, state, name, desc) table.add_row(caret, state, name, desc)
legend = Text.assemble( legend = Text.assemble(
@@ -346,28 +351,42 @@ def render(entries: list[RepoEntry], cursor: int, base: Path, owner: str, status
def clone_one(entry: RepoEntry, base: Path, cfg: Config) -> tuple[RepoEntry, bool, str]: def clone_one(entry: RepoEntry, base: Path, cfg: Config) -> tuple[RepoEntry, bool, str]:
if entry.remote is None: if entry.remote is None:
return entry, False, "sin info remota" return entry, False, "sense info remota"
url = entry.remote.ssh_url if cfg.clone_protocol == "ssh" else entry.remote.clone_url url = entry.remote.ssh_url if cfg.clone_protocol == "ssh" else entry.remote.clone_url
if not url: if not url:
return entry, False, "URL vacía" return entry, False, "URL buida"
target = base / entry.name target = base / entry.name
if target.exists(): if target.exists():
return entry, False, "ya existe en local" return entry, False, "ja existeix en local"
env = {
**os.environ,
"GIT_TERMINAL_PROMPT": "0", # mai demanar credencials interactivament
"GIT_ASKPASS": "/bin/true", # cap helper interactiu
"SSH_ASKPASS": "/bin/true",
}
try: try:
result = subprocess.run( result = subprocess.run(
["git", "clone", "--quiet", url, str(target)], ["git", "clone", "--quiet", url, str(target)],
capture_output=True, capture_output=True,
text=True, text=True,
timeout=600, timeout=600,
stdin=subprocess.DEVNULL,
env=env,
) )
if result.returncode != 0: if result.returncode != 0:
err = (result.stderr or result.stdout or "git clone falló").strip().splitlines() # Netegem la carpeta parcial si git la va crear
return entry, False, err[-1][:80] if err else "error" if target.exists():
shutil.rmtree(target, ignore_errors=True)
err_lines = (result.stderr or result.stdout or "git clone ha fallat").strip().splitlines()
err_msg = " ".join(l.strip() for l in err_lines if l.strip())
return entry, False, err_msg or "error desconegut"
return entry, True, "ok" return entry, True, "ok"
except subprocess.TimeoutExpired: except subprocess.TimeoutExpired:
return entry, False, "timeout" if target.exists():
shutil.rmtree(target, ignore_errors=True)
return entry, False, "timeout (>10 min)"
except FileNotFoundError: except FileNotFoundError:
return entry, False, "git no encontrado" return entry, False, "git no trobat"
def run_clone_queue(entries: list[RepoEntry], base: Path, cfg: Config, live: Live, cursor: int, owner: str) -> int: def run_clone_queue(entries: list[RepoEntry], base: Path, cfg: Config, live: Live, cursor: int, owner: str) -> int: