tawasuyu

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1182Shell 87Rust 63Markdown 541592 Definiciones

mirror git: el poblador reporta el error real de git, no una conjetura

Imprimía siempre «upstream no da el commit» pasara lo que pasara. En la primera tanda marcó así a
diffutils, findutils-xargs y kustomize, cuyos commits se traen a mano sin problema: era transitorio.
Ahora bundle() devuelve (ruta, motivo) con el stderr del paso que falló.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016v9ozVm44p6DB7EMXeZK4o

276efdeced7ef064df6bc4ddd5a370c22d92ee07 · por Sergio · (hace 1 mes) · padre 75b8cdb94 · ver el árbol en este commit

Delta semántico · 3

scripts/fuentes/mirror-git-poblar.py

2 archivos · +35 −12

diff --git a/docs/adr/0013-mirror-de-fuentes.md b/docs/adr/0013-mirror-de-fuentes.md
index f66e0b65..1a421956 100644
--- a/docs/adr/0013-mirror-de-fuentes.md
+++ b/docs/adr/0013-mirror-de-fuentes.md
@@ -205,4 +205,12 @@ eso el silencio se paga con comentarios explícitos en el código.
   hoy eso lo dispara una persona corriendo `mirror-git-poblar.sh`, no el latido.
 - Qué hacer con los repos cuyo servidor **no permite fetch por SHA suelto**
   (`uploadpack.allowReachableSHA1InWant` desactivado): ahí el `--depth 1` del commit exacto falla y
-  hay que caer a un clon completo. El poblador los reporta como `✗ upstream no da el commit`.
+  hay que caer a un clon completo.
+
+  El poblador reporta el **stderr real de git**, no una conjetura. Lo escribí al revés la primera
+  vez — imprimía siempre `✗ upstream no da el commit` — y en la primera tanda marcó así a tres
+  recetas (`diffutils`, `findutils-xargs`, `kustomize`) cuyos commits **sí se traen a mano**. Un
+  fallo en ese punto puede ser el servidor negando el sha, pero también un timeout, DNS o un
+  rate-limit de GitHub, y cada uno se arregla distinto. **Un diagnóstico hardcodeado convierte un
+  fallo transitorio en una conclusión falsa sobre upstream**, que es exactamente el error que ya
+  costó caro en la ruta relativa del bundle.
diff --git a/scripts/fuentes/mirror-git-poblar.py b/scripts/fuentes/mirror-git-poblar.py
index 861cab04..43dc7931 100755
--- a/scripts/fuentes/mirror-git-poblar.py
+++ b/scripts/fuentes/mirror-git-poblar.py
@@ -40,22 +40,37 @@ def ya_en_mirror():
     return {l.strip()[:-7] for l in r.stdout.splitlines() if l.strip().endswith(".bundle")}
 
 def bundle(e, tmpdir):
-    """Clona --depth 1 el commit exacto y emite un bundle. None si upstream no lo da."""
+    """Clona --depth 1 el commit exacto y emite un bundle.
+
+    Devuelve (ruta, None) si sale bien y (None, motivo) si no. El motivo es el stderr REAL de git,
+    no una conjetura: un fallo acá puede ser el servidor negando el fetch por sha suelto, pero
+    tambien un timeout, DNS, o un rate-limit, y cada uno se arregla distinto. Ver ADR 0013.
+    """
     d = os.path.join(tmpdir, "r")
     shutil.rmtree(d, ignore_errors=True)
     os.makedirs(d)
     def git(*a, **kw):
         return subprocess.run(["git", "-C", d] + list(a), capture_output=True, text=True,
                               timeout=kw.get("t", 900))
-    if git("init", "-q").returncode != 0: return None
-    if git("remote", "add", "origin", e["repo"]).returncode != 0: return None
-    # --depth 1 del SHA exacto: el servidor debe permitir fetch por sha (uploadpack.allowReachableSHA1).
-    if git("fetch", "-q", "--depth", "1", "origin", e["commit"], t=1800).returncode != 0:
-        return None
-    if git("update-ref", "refs/heads/hammer", e["commit"]).returncode != 0: return None
+    def paso(etiqueta, *a, **kw):
+        try:
+            r = git(*a, **kw)
+        except subprocess.TimeoutExpired:
+            return f"{etiqueta}: timeout"
+        return None if r.returncode == 0 else f"{etiqueta}: {(r.stderr or r.stdout).strip().splitlines()[-1][:160] if (r.stderr or r.stdout).strip() else 'rc=' + str(r.returncode)}"
+
     out = os.path.join(tmpdir, f'{e["commit"]}.bundle')
-    if git("bundle", "create", out, "refs/heads/hammer", t=1800).returncode != 0: return None
-    return out if os.path.exists(out) else None
+    for etiqueta, args, kw in (
+        ("init",      ("init", "-q"), {}),
+        ("remote",    ("remote", "add", "origin", e["repo"]), {}),
+        # --depth 1 del SHA exacto: el servidor debe permitir fetch por sha (uploadpack.allowReachableSHA1).
+        ("fetch",     ("fetch", "-q", "--depth", "1", "origin", e["commit"]), {"t": 1800}),
+        ("update-ref",("update-ref", "refs/heads/hammer", e["commit"]), {}),
+        ("bundle",    ("bundle", "create", out, "refs/heads/hammer"), {"t": 1800}),
+    ):
+        motivo = paso(etiqueta, *args, **kw)
+        if motivo: return None, motivo
+    return (out, None) if os.path.exists(out) else (None, "bundle: git dijo OK pero no hay fichero")
 
 def main():
     fs = fuentes_git()
@@ -76,10 +91,10 @@ def main():
     tmpdir = tempfile.mkdtemp(prefix="hammer-git-mirror-")
     try:
         for i, e in enumerate(faltan, 1):
-            b = bundle(e, tmpdir)
+            b, motivo = bundle(e, tmpdir)
             if not b:
                 fallo += 1
-                print(f"  [{i}/{len(faltan)}] {e['receta']:<24} ✗ upstream no da el commit", flush=True)
+                print(f"  [{i}/{len(faltan)}] {e['receta']:<24} ✗ {motivo}", flush=True)
                 continue
             mb = os.path.getsize(b) / 1e6
             r = subprocess.run(["rsync", "-a", "-e", " ".join(SSH), b,