From 276efdeced7ef064df6bc4ddd5a370c22d92ee07 Mon Sep 17 00:00:00 2001 From: Sergio Date: Wed, 26 Aug 2026 21:07:10 +0000 Subject: [PATCH] mirror git: el poblador reporta el error real de git, no una conjetura MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Claude-Session: https://claude.ai/code/session_016v9ozVm44p6DB7EMXeZK4o --- docs/adr/0013-mirror-de-fuentes.md | 10 +++++++- scripts/fuentes/mirror-git-poblar.py | 37 +++++++++++++++++++--------- 2 files changed, 35 insertions(+), 12 deletions(-) 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,